///////////////////////////////////////////////////////////////////////////////
//
// 3DimViewer
// Lightweight 3D DICOM viewer.
//
// Copyright 2008-2016 3Dim Laboratory s.r.o.
// Copyright 2016-2018 Tescan 3Dim s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Includes

#ifdef _OPENMP
#include <omp.h>
#endif

#include <alg/CMarchingCubes.h>
#include <base/stack_allocator.hpp>
#include <VPL/Base/Logging.h>

#include <chrono>

////////////////////////////////////////////////////////////
// scheme cubes with numbering conventions of nodes and edges

/*       128 |7|----------10---------|6| 64
            / |                     / |
           /  |                    /  |
         11   |                   9   |
         /    |                  /    |
        /     7                 /     6
       /      |                /      |
  16 |4|----------8----------|5| 32   |
      |       |               |       |
      |       |               |       |
      |       |               |       |
      |    8 |3|----------2---|------|2| 4
      4      /                5      /
      |     /                 |     /
      |    3                  |    1
      |   /                   |   /
      |  /                    |  /
      | /                     | /
   1 |0|----------0----------|1| 2        */

// array for all 256 combinations of nodes states, have new triangles vertex edge indexes 

const int node_state[256][12] = { 
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},   // 0 C
    {0, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 1
    {1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 2
    {5, 1, 3, 3, 4, 5, -1, -1, -1, -1, -1, -1},         // 3
    {2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 4
    {0, 3, 4, 2, 1, 6, -1, -1, -1, -1, -1, -1},         // 5
    {6, 2, 0, 0, 5, 6, -1, -1, -1, -1, -1, -1},         // 6
    {3, 4, 6, 6, 2, 3, 4, 5, 6, -1, -1, -1},            // 7
    {3, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 8
    {4, 0, 2, 2, 7, 4, -1, -1, -1, -1, -1, -1},         // 9
    {1, 0, 5, 3, 2, 7, -1, -1, -1, -1, -1, -1},         // 10
    {2, 7, 5, 5, 1, 2, 7, 4, 5, -1, -1, -1},            // 11
    {7, 3, 6, 3, 1, 6, -1, -1, -1, -1, -1, -1},         // 12
    {1, 6, 4, 4, 0, 1, 6, 7, 4, -1, -1, -1},            // 13
    {0, 5, 7, 7, 3, 0, 5, 6, 7, -1, -1, -1},            // 14
    {4, 5, 6, 6, 7, 4, -1, -1, -1, -1, -1, -1},         // 15
    {11, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1},     // 16
    {0, 3, 11, 11, 8, 0, -1, -1, -1, -1, -1, -1},       // 17
    {1, 0, 5, 11, 8, 4, -1, -1, -1, -1, -1, -1},        // 18
    {5, 1, 11, 11, 8, 5, 1, 3, 11, -1, -1, -1},         // 19
    {2, 1, 6, 11, 8, 4, -1, -1, -1, -1, -1, -1},        // 20
    {2, 1, 6, 0, 3, 11, 11, 8, 0, -1, -1, -1},          // 21
    {11, 8, 4, 6, 2, 0, 0, 5, 6, -1, -1, -1},           // 22
    {8, 5, 11, 5, 6, 2, 5, 2, 11, 2, 3, 11},            // 23
    {3, 2, 7, 11, 8, 4, -1, -1, -1, -1, -1, -1},        // 24
    {11, 8, 2, 2, 7, 11, 8, 0, 2, -1, -1, -1},          // 25
    {1, 0, 5, 3, 2, 7, 11, 8, 4, -1, -1, -1},           // 26
    {8, 5, 7, 5, 1, 2, 5, 2, 7, 7, 11, 8},              // 27
    {11, 8, 4, 7, 3, 1, 1, 6, 7, -1, -1, -1},           // 28
    {0, 1, 8, 1, 6, 7, 1, 7, 8, 7, 11, 8},              // 29
    {11, 8, 4, 0, 5, 7, 7, 3, 0, 5, 6, 7},              // 30
    {11, 5, 7, 5, 11, 8, 7, 5, 6, -1, -1, -1},          // 31 C
    {8, 9, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 32
    {0, 3, 4, 8, 9, 5, -1, -1, -1, -1, -1, -1},         // 33
    {8, 9, 1, 1, 0, 8, -1, -1, -1, -1, -1, -1},         // 34
    {8, 9, 3, 3, 4, 8, 9, 1, 3, -1, -1, -1},            // 35
    {2, 1, 6, 8, 9, 5, -1, -1, -1, -1, -1, -1},         // 36
    {0, 3, 4, 2, 1, 6, 8, 9, 5, -1, -1, -1},            // 37
    {6, 2, 8, 8, 9, 6, 2, 0, 8, -1, -1, -1},            // 38
    {9, 6, 4, 6, 2, 3, 6, 3, 4, 4, 8, 9},               // 39
    {3, 2, 7, 8, 9, 5, -1, -1, -1, -1, -1, -1},         // 40
    {8, 9, 5, 4, 0, 2, 2, 7, 4, -1, -1, -1},            // 41
    {3, 2, 7, 8, 9, 1, 1, 0, 8, -1, -1, -1},            // 42
    {2, 7, 1, 7, 4, 8, 7, 8, 1, 8, 9, 1},               // 43
    {8, 9, 5, 7, 3, 1, 1, 6, 7, -1, -1, -1},            // 44
    {8, 9, 5, 1, 6, 4, 4, 0, 1, 6, 7, 4},               // 45
    {9, 6, 8, 6, 7, 3, 6, 3, 8, 3, 0, 8},               // 46
    {8, 6, 4, 6, 8, 9, 4, 6, 7, -1, -1, -1},            // 47 C
    {4, 11, 9, 9, 5, 4, -1, -1, -1, -1, -1, -1},        // 48
    {0, 3, 9, 9, 5, 0, 3, 11, 9, -1, -1, -1},           // 49
    {4, 11, 1, 1, 0, 4, 11, 9, 1, -1, -1, -1},          // 50
    {3, 11, 9, 9, 1, 3, -1, -1, -1, -1, -1, -1},        // 51
    {2, 1, 6, 4, 11, 9, 9, 5, 4, -1, -1, -1},           // 52
    {2, 1, 6, 0, 3, 9, 9, 5, 0, 3, 11, 9},              // 53
    {9, 6, 11, 6, 2, 0, 6, 0, 11, 0, 4, 11},            // 54
    {6, 3, 9, 3, 6, 2, 9, 3, 11, -1, -1, -1},           // 55 C
    {3, 2, 7, 4, 11, 9, 9, 5, 4, -1, -1, -1},           // 56
    {5, 0, 9, 0, 2, 7, 0, 7, 9, 7, 11, 9},              // 57
    {3, 2, 7, 4, 11, 1, 1, 0, 4, 11, 9, 1},             // 58
    {2, 11, 1, 11, 2, 7, 1, 11, 9, -1, -1, -1},         // 59 C
    {7, 3, 1, 1, 6, 7, 4, 11, 9, 9, 5, 4},              // 60
    {1, 5, 0, 6, 11, 9, 11, 6, 7, -1, -1, -1},          // 61 C
    {0, 4, 3, 6, 11, 9, 11, 6, 7, -1, -1, -1},          // 62 C
    {6, 7, 9, 9, 7, 11, -1, -1, -1, -1, -1, -1},        // 63 C
    {9, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1},     // 64
    {0, 3, 4, 9, 10, 6, -1, -1, -1, -1, -1, -1},        // 65
    {1, 0, 5, 9, 10, 6, -1, -1, -1, -1, -1, -1},        // 66
    {9, 10, 6, 5, 1, 3, 3, 4, 5, -1, -1, -1},           // 67
    {2, 1, 10, 1, 9, 10, -1, -1, -1, -1, -1, -1},       // 68
    {0, 3, 4, 2, 1, 9, 9, 10, 2, -1, -1, -1},           // 69
    {9, 10, 0, 0, 5, 9, 10, 2, 0, -1, -1, -1},          // 70
    {3, 4, 2, 4, 5, 9, 4, 9, 2, 9, 10, 2},              // 71
    {3, 2, 7, 9, 10, 6, -1, -1, -1, -1, -1, -1},        // 72
    {9, 10, 6, 4, 0, 2, 2, 7, 4, -1, -1, -1},           // 73
    {1, 0, 5, 3, 2, 7, 9, 10, 6, -1, -1, -1},           // 74
    {9, 10, 6, 2, 7, 5, 5, 1, 2, 7, 4, 5},              // 75
    {7, 3, 9, 9, 10, 7, 3, 1, 9, -1, -1, -1},           // 76
    {10, 7, 9, 7, 4, 0, 7, 0, 9, 0, 1, 9},              // 77
    {10, 7, 5, 7, 3, 0, 7, 0, 5, 5, 9, 10},             // 78
    {9, 7, 5, 7, 9, 10, 5, 7, 4, -1, -1, -1},           // 79 C
    {11, 8, 4, 9, 10, 6, -1, -1, -1, -1, -1, -1},       // 80
    {9, 10, 6, 0, 3, 11, 11, 8, 0, -1, -1, -1},         // 81
    {1, 0, 5, 11, 8, 4, 9, 10, 6, -1, -1, -1},          // 82
    {9, 10, 6, 5, 1, 11, 11, 8, 5, 1, 3, 11},           // 83
    {11, 8, 4, 2, 1, 9, 9, 10, 2, -1, -1, -1},          // 84
    {0, 3, 11, 11, 8, 0, 2, 1, 9, 9, 10, 2},            // 85
    {11, 8, 4, 9, 10, 0, 0, 5, 9, 10, 2, 0},            // 86
    {8, 5, 9, 10, 3, 11, 3, 10, 2, -1, -1, -1},         // 87 C
    {3, 2, 7, 11, 8, 4, 9, 10, 6, -1, -1, -1},          // 88
    {9, 10, 6, 11, 8, 2, 2, 7, 11, 8, 0, 2},            // 89
    {1, 0, 5, 3, 2, 7, 11, 8, 4, 9, 10, 6},             // 90
    {2, 6, 1, 8, 5, 9, 10, 7, 11, -1, -1, -1},          // 91 C
    {11, 8, 4, 7, 3, 9, 9, 10, 7, 3, 1, 9},             // 92
    {10, 7, 11, 8, 1, 9, 1, 8, 0, -1, -1, -1},          // 93 C
    {0, 4, 3, 8, 5, 9, 10, 7, 11, -1, -1, -1},          // 94 C
    {8, 5, 9, 10, 7, 11, -1, -1, -1, -1, -1, -1},       // 95 C
    {5, 8, 6, 8, 10, 6, -1, -1, -1, -1, -1, -1},        // 96
    {0, 3, 4, 5, 8, 10, 10, 6, 5, -1, -1, -1},          // 97
    {1, 0, 10, 10, 6, 1, 0, 8, 10, -1, -1, -1},         // 98
    {6, 1, 10, 1, 3, 4, 1, 4, 10, 4, 8, 10},            // 99
    {5, 8, 2, 2, 1, 5, 8, 10, 2, -1, -1, -1},           // 100
    {0, 3, 4, 5, 8, 2, 2, 1, 5, 8, 10, 2},              // 101
    {0, 8, 10, 10, 2, 0, -1, -1, -1, -1, -1, -1},       // 102
    {3, 8, 2, 8, 3, 4, 2, 8, 10, -1, -1, -1},           // 103 C
    {3, 2, 7, 5, 8, 10, 10, 6, 5, -1, -1, -1},          // 104
    {4, 0, 2, 2, 7, 4, 5, 8, 10, 10, 6, 5},             // 105
    {3, 2, 7, 1, 0, 10, 10, 6, 1, 0, 8, 10},            // 106
    {2, 6, 1, 7, 8, 10, 8, 7, 4, -1, -1, -1},           // 107 C
    {7, 3, 10, 3, 1, 5, 3, 5, 10, 5, 8, 10},            // 108
    {1, 5, 0, 7, 8, 10, 8, 7, 4, -1, -1, -1},           // 109 C
    {7, 0, 10, 0, 7, 3, 10, 0, 8, -1, -1, -1},          // 110 C
    {7, 4, 10, 10, 4, 8, -1, -1, -1, -1, -1, -1},       // 111 C
    {10, 6, 4, 4, 11, 10, 6, 5, 4, -1, -1, -1},         // 112
    {10, 6, 11, 6, 5, 0, 6, 0, 11, 0, 3, 11},           // 113
    {0, 4, 6, 4, 11, 10, 4, 10, 6, 6, 1, 0},            // 114
    {10, 1, 11, 1, 10, 6, 11, 1, 3, -1, -1, -1},        // 115 C
    {1, 5, 2, 5, 4, 11, 5, 11, 2, 11, 10, 2},           // 116
    {1, 5, 0, 10, 3, 11, 3, 10, 2, -1, -1, -1},         // 117 C
    {4, 10, 0, 10, 4, 11, 0, 10, 2, -1, -1, -1},        // 118 C
    {10, 2, 11, 11, 2, 3, -1, -1, -1, -1, -1, -1},      // 119 C
    {3, 2, 7, 10, 6, 4, 4, 11, 10, 6, 5, 4},            // 120
    {10, 7, 11, 6, 0, 2, 0, 6, 5, -1, -1, -1},          // 121 C
    {0, 4, 3, 2, 6, 1, 10, 7, 11, -1, -1, -1},          // 122 C
    {2, 6, 1, 10, 7, 11, -1, -1, -1, -1, -1, -1},       // 123 C
    {10, 7, 11, 5, 3, 1, 3, 5, 4, -1, -1, -1},          // 124 C
    {1, 5, 0, 10, 7, 11, -1, -1, -1, -1, -1, -1},       // 125 C
    {0, 4, 3, 10, 7, 11, -1, -1, -1, -1, -1, -1},       // 126 C
    {10, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1},    // 127 C
    {10, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1},    // 128
    {0, 3, 4, 10, 11, 7, -1, -1, -1, -1, -1, -1},       // 129
    {1, 0, 5, 10, 11, 7, -1, -1, -1, -1, -1, -1},       // 130
    {10, 11, 7, 5, 1, 3, 3, 4, 5, -1, -1, -1},          // 131
    {2, 1, 6, 10, 11, 7, -1, -1, -1, -1, -1, -1},       // 132
    {0, 3, 4, 2, 1, 6, 10, 11, 7, -1, -1, -1},          // 133
    {10, 11, 7, 6, 2, 0, 0, 5, 6, -1, -1, -1},          // 134
    {10, 11, 7, 3, 4, 6, 6, 2, 3, 4, 5, 6},             // 135
    {10, 11, 2, 11, 3, 2, -1, -1, -1, -1, -1, -1},      // 136
    {4, 0, 10, 10, 11, 4, 0, 2, 10, -1, -1, -1},        // 137
    {1, 0, 5, 10, 11, 3, 3, 2, 10, -1, -1, -1},         // 138
    {11, 4, 10, 4, 5, 1, 4, 1, 10, 1, 2, 10},           // 139
    {10, 11, 1, 1, 6, 10, 11, 3, 1, -1, -1, -1},        // 140
    {11, 4, 6, 4, 0, 1, 4, 1, 6, 6, 10, 11},            // 141
    {10, 11, 6, 11, 3, 0, 11, 0, 6, 0, 5, 6},           // 142
    {10, 4, 6, 4, 10, 11, 6, 4, 5, -1, -1, -1},         // 143 C
    {7, 10, 4, 10, 8, 4, -1, -1, -1, -1, -1, -1},       // 144
    {7, 10, 0, 0, 3, 7, 10, 8, 0, -1, -1, -1},          // 145
    {1, 0, 5, 7, 10, 8, 8, 4, 7, -1, -1, -1},           // 146
    {5, 1, 8, 1, 3, 7, 1, 7, 8, 7, 10, 8},              // 147
    {2, 1, 6, 7, 10, 8, 8, 4, 7, -1, -1, -1},           // 148
    {2, 1, 6, 7, 10, 0, 0, 3, 7, 10, 8, 0},             // 149
    {6, 2, 0, 0, 5, 6, 7, 10, 8, 8, 4, 7},              // 150
    {3, 7, 2, 5, 10, 8, 10, 5, 6, -1, -1, -1},          // 151 C
    {3, 2, 8, 8, 4, 3, 2, 10, 8, -1, -1, -1},           // 152
    {0, 2, 10, 10, 8, 0, -1, -1, -1, -1, -1, -1},       // 153
    {1, 0, 5, 3, 2, 8, 8, 4, 3, 2, 10, 8},              // 154
    {5, 2, 8, 2, 5, 1, 8, 2, 10, -1, -1, -1},           // 155 C
    {6, 10, 1, 10, 8, 4, 10, 4, 1, 4, 3, 1},            // 156
    {1, 10, 0, 10, 1, 6, 0, 10, 8, -1, -1, -1},         // 157 C
    {0, 4, 3, 5, 10, 8, 10, 5, 6, -1, -1, -1},          // 158 C
    {5, 6, 8, 8, 6, 10, -1, -1, -1, -1, -1, -1},        // 159 C
    {8, 9, 5, 10, 11, 7, -1, -1, -1, -1, -1, -1},       // 160
    {0, 3, 4, 8, 9, 5, 10, 11, 7, -1, -1, -1},          // 161
    {10, 11, 7, 8, 9, 1, 1, 0, 8, -1, -1, -1},          // 162
    {10, 11, 7, 8, 9, 3, 3, 4, 8, 9, 1, 3},             // 163
    {2, 1, 6, 8, 9, 5, 10, 11, 7, -1, -1, -1},          // 164
    {0, 3, 4, 2, 1, 6, 8, 9, 5, 10, 11, 7},             // 165
    {10, 11, 7, 6, 2, 8, 8, 9, 6, 2, 0, 8},             // 166
    {3, 7, 2, 11, 4, 8, 9, 6, 10, -1, -1, -1},          // 167 C
    {8, 9, 5, 10, 11, 3, 3, 2, 10, -1, -1, -1},         // 168
    {8, 9, 5, 4, 0, 10, 10, 11, 4, 0, 2, 10},           // 169
    {8, 9, 1, 1, 0, 8, 10, 11, 3, 3, 2, 10},            // 170
    {11, 4, 8, 2, 9, 1, 9, 2, 10, -1, -1, -1},          // 171 C
    {8, 9, 5, 10, 11, 1, 1, 6, 10, 11, 3, 1},           // 172
    {1, 5, 0, 11, 4, 8, 9, 6, 10, -1, -1, -1},          // 173 C
    {9, 6, 10, 0, 11, 3, 11, 0, 8, -1, -1, -1},         // 174 C
    {11, 4, 8, 9, 6, 10, -1, -1, -1, -1, -1, -1},       // 175 C
    {9, 5, 7, 7, 10, 9, 5, 4, 7, -1, -1, -1},           // 176
    {3, 7, 5, 7, 10, 9, 7, 9, 5, 5, 0, 3},              // 177
    {10, 9, 7, 9, 1, 0, 9, 0, 7, 0, 4, 7},              // 178
    {7, 9, 3, 9, 7, 10, 3, 9, 1, -1, -1, -1},           // 179 C
    {2, 1, 6, 9, 5, 7, 7, 10, 9, 5, 4, 7},              // 180
    {1, 5, 0, 3, 7, 2, 9, 6, 10, -1, -1, -1},           // 181 C
    {9, 6, 10, 4, 2, 0, 2, 4, 7, -1, -1, -1},           // 182 C
    {3, 7, 2, 9, 6, 10, -1, -1, -1, -1, -1, -1},        // 183 C
    {9, 5, 10, 5, 4, 3, 5, 3, 10, 3, 2, 10},            // 184
    {9, 0, 10, 0, 9, 5, 10, 0, 2, -1, -1, -1},          // 185 C
    {0, 4, 3, 2, 9, 1, 9, 2, 10, -1, -1, -1},           // 186 C
    {2, 10, 1, 1, 10, 9, -1, -1, -1, -1, -1, -1},       // 187 C
    {9, 6, 10, 5, 3, 1, 3, 5, 4, -1, -1, -1},           // 188 C
    {1, 5, 0, 9, 6, 10, -1, -1, -1, -1, -1, -1},        // 189 C
    {0, 4, 3, 9, 6, 10, -1, -1, -1, -1, -1, -1},        // 190 C
    {9, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1},     // 191 C
    {6, 9, 7, 9, 11, 7, -1, -1, -1, -1, -1, -1},        // 192
    {0, 3, 4, 6, 9, 11, 11, 7, 6, -1, -1, -1},          // 193
    {1, 0, 5, 6, 9, 11, 11, 7, 6, -1, -1, -1},          // 194
    {5, 1, 3, 3, 4, 5, 6, 9, 11, 11, 7, 6},             // 195
    {2, 1, 11, 11, 7, 2, 1, 9, 11, -1, -1, -1},         // 196
    {0, 3, 4, 2, 1, 11, 11, 7, 2, 1, 9, 11},            // 197
    {5, 9, 0, 9, 11, 7, 9, 7, 0, 7, 2, 0},              // 198
    {3, 7, 2, 4, 9, 11, 9, 4, 5, -1, -1, -1},           // 199 C
    {6, 9, 3, 3, 2, 6, 9, 11, 3, -1, -1, -1},           // 200
    {6, 9, 2, 9, 11, 4, 9, 4, 2, 4, 0, 2},              // 201
    {1, 0, 5, 6, 9, 3, 3, 2, 6, 9, 11, 3},              // 202
    {2, 6, 1, 4, 9, 11, 9, 4, 5, -1, -1, -1},           // 203 C
    {3, 1, 9, 9, 11, 3, -1, -1, -1, -1, -1, -1},        // 204
    {4, 1, 11, 1, 4, 0, 11, 1, 9, -1, -1, -1},          // 205 C
    {0, 9, 3, 9, 0, 5, 3, 9, 11, -1, -1, -1},           // 206 C
    {4, 9, 11, 9, 4, 5, -1, -1, -1, -1, -1, -1},        // 207 C
    {8, 4, 6, 6, 9, 8, 4, 7, 6, -1, -1, -1},            // 208
    {9, 8, 6, 8, 0, 3, 8, 3, 6, 3, 7, 6},               // 209
    {1, 0, 5, 8, 4, 6, 6, 9, 8, 4, 7, 6},               // 210
    {8, 5, 9, 7, 1, 3, 1, 7, 6, -1, -1, -1},            // 211 C
    {2, 1, 7, 1, 9, 8, 1, 8, 7, 8, 4, 7},               // 212
    {3, 7, 2, 8, 1, 9, 1, 8, 0, -1, -1, -1},            // 213 C
    {8, 5, 9, 4, 2, 0, 2, 4, 7, -1, -1, -1},            // 214 C
    {3, 7, 2, 8, 5, 9, -1, -1, -1, -1, -1, -1},         // 215 C
    {2, 6, 4, 6, 9, 8, 6, 8, 4, 4, 3, 2},               // 216
    {6, 8, 2, 8, 6, 9, 2, 8, 0, -1, -1, -1},            // 217 C
    {0, 4, 3, 2, 6, 1, 8, 5, 9, -1, -1, -1},            // 218 C
    {2, 6, 1, 8, 5, 9, -1, -1, -1, -1, -1, -1},         // 219 C
    {8, 3, 9, 3, 8, 4, 9, 3, 1, -1, -1, -1},            // 220 C
    {8, 1, 9, 1, 8, 0, -1, -1, -1, -1, -1, -1},         // 221 C
    {0, 4, 3, 8, 5, 9, -1, -1, -1, -1, -1, -1},         // 222 C
    {8, 5, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 223 C
    {11, 7, 5, 5, 8, 11, 7, 6, 5, -1, -1, -1},          // 224
    {0, 3, 4, 11, 7, 5, 5, 8, 11, 7, 6, 5},             // 225
    {11, 7, 8, 7, 6, 1, 7, 1, 8, 1, 0, 8},              // 226
    {11, 4, 8, 7, 1, 3, 1, 7, 6, -1, -1, -1},           // 227 C
    {1, 5, 7, 5, 8, 11, 5, 11, 7, 7, 2, 1},             // 228
    {1, 5, 0, 3, 7, 2, 11, 4, 8, -1, -1, -1},           // 229 C
    {11, 2, 8, 2, 11, 7, 8, 2, 0, -1, -1, -1},          // 230 C
    {3, 7, 2, 11, 4, 8, -1, -1, -1, -1, -1, -1},        // 231 C
    {8, 11, 5, 11, 3, 2, 11, 2, 5, 2, 6, 5},            // 232
    {11, 4, 8, 6, 0, 2, 0, 6, 5, -1, -1, -1},           // 233 C
    {2, 6, 1, 0, 11, 3, 11, 0, 8, -1, -1, -1},          // 234 C
    {2, 6, 1, 11, 4, 8, -1, -1, -1, -1, -1, -1},        // 235 C
    {5, 11, 1, 11, 5, 8, 1, 11, 3, -1, -1, -1},         // 236 C
    {1, 5, 0, 11, 4, 8, -1, -1, -1, -1, -1, -1},        // 237 C
    {0, 11, 3, 11, 0, 8, -1, -1, -1, -1, -1, -1},       // 238 C
    {11, 4, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1},     // 239 C
    {4, 6, 5, 6, 4, 7, -1, -1, -1, -1, -1, -1},         // 240
    {0, 7, 5, 7, 0, 3, 5, 7, 6, -1, -1, -1},            // 241 C
    {1, 4, 6, 4, 1, 0, 6, 4, 7, -1, -1, -1},            // 242 C
    {7, 6, 3, 3, 6, 1, -1, -1, -1, -1, -1, -1},         // 243 C
    {2, 5, 7, 5, 2, 1, 7, 5, 4, -1, -1, -1},            // 244 C
    {1, 5, 0, 3, 7, 2, -1, -1, -1, -1, -1, -1},         // 245 C
    {4, 2, 0, 2, 4, 7, -1, -1, -1, -1, -1, -1},         // 246 C
    {3, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 247 C
    {3, 6, 4, 6, 3, 2, 4, 6, 5, -1, -1, -1},            // 248 C
    {6, 0, 2, 0, 6, 5, -1, -1, -1, -1, -1, -1},         // 249 C
    {0, 4, 3, 2, 6, 1, -1, -1, -1, -1, -1, -1},         // 250 C
    {2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 251 C
    {5, 3, 1, 3, 5, 4, -1, -1, -1, -1, -1, -1},         // 252 C
    {1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 253 C
    {0, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1},      // 254 C
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},   // 255 C
};

const short tri_direction_group[256][4] = {
    { -1, -1, -1, -1 },
    {  0, -1, -1, -1 },
    {  1, -1, -1, -1 },
    {  2,  2, -1, -1 },
    {  3, -1, -1, -1 },
    {  0,  3, -1, -1 },
    {  4,  4, -1, -1 },
    {  1,  1,  5, -1 },
    {  6, -1, -1, -1 },
    {  7,  7, -1, -1 },
    {  1,  6, -1, -1 },
    {  0,  0,  5, -1 },
    {  8,  8, -1, -1 },
    {  6,  6,  5, -1 },
    {  3,  3,  5, -1 },
    {  5,  5, -1, -1 },
    {  9, -1, -1, -1 },
    { 10, 10, -1, -1 },
    {  1,  9, -1, -1 },
    {  0,  0, 11, -1 },
    {  3,  9, -1, -1 },
    {  3, 10, 10, -1 },
    {  9,  4,  4, -1 },
    {  0,  4,  0, 12 },
    {  6,  9, -1, -1 },
    {  0,  0, 13, -1 },
    {  1,  6,  9, -1 },
    {  0,  0,  0,  0 },
    {  9,  8,  8, -1 },
    { 14,  8,  0,  0 },
    {  9,  3,  3,  5 },
    {  0,  0,  5, -1 },
    { 15, -1, -1, -1 },
    {  0, 15, -1, -1 },
    { 12, 12, -1, -1 },
    {  1,  1, 11, -1 },
    {  3, 15, -1, -1 },
    {  0,  3, 15, -1 },
    {  1,  1, 16, -1 },
    {  1,  1,  1,  1 },
    {  6, 15, -1, -1 },
    { 15,  7,  7, -1 },
    {  6, 12, 12, -1 },
    {  0,  4,  0, 12 },
    { 15,  8,  8, -1 },
    { 15,  6,  6,  5 },
    {  1,  8,  1, 17 },
    {  1,  1,  5, -1 },
    { 18, 18, -1, -1 },
    {  9,  9, 11, -1 },
    { 15, 15, 11, -1 },
    { 11, 11, -1, -1 },
    {  3, 18, 18, -1 },
    {  3,  9,  9, 11 },
    {  2,  4, 15, 15 },
    {  1,  1, 11, -1 },
    {  6, 18, 18, -1 },
    {  9,  7,  9,  2 },
    {  6, 15, 15, 11 },
    {  0,  0, 11, -1 },
    {  8,  8, 18, 18 },
    { 19,  2,  2, -1 },
    { 20,  2,  2, -1 },
    {  2,  2, -1, -1 },
    { 20, -1, -1, -1 },
    {  0, 20, -1, -1 },
    {  1, 20, -1, -1 },
    { 20,  2,  2, -1 },
    { 17, 17, -1, -1 },
    {  0, 17, 17, -1 },
    {  3,  3, 16, -1 },
    {  1,  8,  1, 17 },
    {  6, 20, -1, -1 },
    { 20,  7,  7, -1 },
    {  1,  6, 20, -1 },
    { 20,  0,  0,  5 },
    {  3,  3, 21, -1 },
    {  3,  7,  3, 14 },
    {  3,  3,  3,  3 },
    {  3,  3,  5, -1 },
    {  9, 20, -1, -1 },
    { 20, 10, 10, -1 },
    {  1,  9, 20, -1 },
    { 20,  0,  0, 11 },
    {  9, 17, 17, -1 },
    { 10, 10, 17, 17 },
    {  9,  3,  3, 16 },
    {  6, 12, 12, -1 },
    {  6,  9, 20, -1 },
    { 20,  0,  0, 13 },
    {  1,  6,  9, 20 },
    {  9,  6,  1, -1 },
    {  9,  3,  3, 21 },
    {  1, 14, 14, -1 },
    { 20,  6,  1, -1 },
    {  6,  1, -1, -1 },
    { 22, 22, -1, -1 },
    {  0, 22, 22, -1 },
    { 15, 15, 16, -1 },
    { 15,  2, 15,  4 },
    { 20, 20, 16, -1 },
    {  0, 20, 20, 16 },
    { 16, 16, -1, -1 },
    {  1,  1, 16, -1 },
    {  6, 22, 22, -1 },
    {  7,  7, 22, 22 },
    {  6, 15, 15, 16 },
    {  9,  4,  4, -1 },
    {  3, 23,  3, 22 },
    { 19,  4,  4, -1 },
    {  3,  3, 16, -1 },
    {  4,  4, -1, -1 },
    { 15, 15, 24, -1 },
    { 15, 25, 15, 10 },
    { 15, 15, 15, 15 },
    { 15, 15, 11, -1 },
    { 20, 18, 20, 12 },
    { 19, 12, 12, -1 },
    { 15, 15, 16, -1 },
    { 12, 12, -1, -1 },
    {  6, 15, 15, 24 },
    {  1, 25, 25, -1 },
    { 20,  9,  1, -1 },
    {  9,  1, -1, -1 },
    {  1, 23, 23, -1 },
    { 19,  1, -1, -1 },
    { 20,  1, -1, -1 },
    {  1, -1, -1, -1 },
    { 19, -1, -1, -1 },
    {  0, 19, -1, -1 },
    {  1, 19, -1, -1 },
    { 19,  2,  2, -1 },
    {  3, 19, -1, -1 },
    {  0,  3, 19, -1 },
    { 19,  4,  4, -1 },
    { 19,  1,  1,  5 },
    { 14, 14, -1, -1 },
    {  6,  6, 13, -1 },
    {  1, 14, 14, -1 },
    {  6,  2,  6, 10 },
    {  6,  6, 21, -1 },
    {  6,  6,  6,  6 },
    {  6, 17,  6,  4 },
    {  6,  6,  5, -1 },
    { 25, 25, -1, -1 },
    {  9,  9, 13, -1 },
    {  1, 25, 25, -1 },
    {  0, 18,  0, 25 },
    {  3, 25, 25, -1 },
    {  3,  9,  9, 13 },
    {  4,  4, 25, 25 },
    { 15,  7,  7, -1 },
    { 19, 19, 13, -1 },
    { 13, 13, -1, -1 },
    {  1, 19, 19, 13 },
    {  0,  0, 13, -1 },
    {  6, 25,  6, 23 },
    {  6,  6, 13, -1 },
    { 20,  7,  7, -1 },
    {  7,  7, -1, -1 },
    { 15, 19, -1, -1 },
    {  0, 15, 19, -1 },
    { 19, 12, 12, -1 },
    { 19,  1,  1, 11 },
    {  3, 15, 19, -1 },
    {  0,  3, 15, 19 },
    { 19,  1,  1, 16 },
    { 15,  3,  0, -1 },
    { 15, 14, 14, -1 },
    { 15,  6,  6, 13 },
    { 12, 12, 14, 14 },
    {  3, 10, 10, -1 },
    { 15,  6,  6, 21 },
    { 19,  3,  0, -1 },
    {  0, 17, 17, -1 },
    {  3,  0, -1, -1 },
    {  9,  9, 24, -1 },
    {  9,  9,  9,  9 },
    {  9, 12,  9, 22 },
    {  9,  9, 11, -1 },
    {  3,  9,  9, 24 },
    { 19, 15,  0, -1 },
    {  0, 22, 22, -1 },
    { 15,  0, -1, -1 },
    {  9, 23,  9, 14 },
    {  9,  9, 13, -1 },
    { 20, 10, 10, -1 },
    { 10, 10, -1, -1 },
    {  0, 23, 23, -1 },
    { 19,  0, -1, -1 },
    { 20,  0, -1, -1 },
    {  0, -1, -1, -1 },
    { 23, 23, -1, -1 },
    {  0, 23, 23, -1 },
    {  1, 23, 23, -1 },
    {  2,  2, 23, 23 },
    { 20, 20, 21, -1 },
    {  0, 20, 20, 21 },
    {  3, 23,  3, 22 },
    { 15,  8,  8, -1 },
    { 19, 19, 21, -1 },
    { 19,  8, 19,  7 },
    {  1, 19, 19, 21 },
    {  9,  8,  8, -1 },
    { 21, 21, -1, -1 },
    {  6,  6, 21, -1 },
    {  3,  3, 21, -1 },
    {  8,  8, -1, -1 },
    { 19, 19, 24, -1 },
    { 19, 10, 19, 18 },
    {  1, 19, 19, 24 },
    {  6, 18, 18, -1 },
    { 20, 14, 20, 25 },
    { 15, 14, 14, -1 },
    {  6, 22, 22, -1 },
    { 15,  6, -1, -1 },
    { 19, 19, 19, 19 },
    { 19, 19, 13, -1 },
    { 20,  9,  6, -1 },
    {  9,  6, -1, -1 },
    { 19, 19, 21, -1 },
    { 14, 14, -1, -1 },
    { 20,  6, -1, -1 },
    {  6, -1, -1, -1 },
    { 20, 20, 24, -1 },
    {  0, 20, 20, 24 },
    { 20, 18, 20, 12 },
    {  3, 18, 18, -1 },
    { 20, 20, 20, 20 },
    { 19, 15,  3, -1 },
    { 20, 20, 16, -1 },
    { 15,  3, -1, -1 },
    { 20, 14, 20, 25 },
    {  3, 25, 25, -1 },
    {  9, 17, 17, -1 },
    {  9,  3, -1, -1 },
    { 20, 20, 21, -1 },
    { 19,  3, -1, -1 },
    { 17, 17, -1, -1 },
    {  3, -1, -1, -1 },
    { 24, 24, -1, -1 },
    {  9,  9, 24, -1 },
    { 15, 15, 24, -1 },
    { 18, 18, -1, -1 },
    { 20, 20, 24, -1 },
    { 19, 15, -1, -1 },
    { 22, 22, -1, -1 },
    { 15, -1, -1, -1 },
    { 19, 19, 24, -1 },
    { 25, 25, -1, -1 },
    { 20,  9, -1, -1 },
    {  9, -1, -1, -1 },
    { 23, 23, -1, -1 },
    { 19, -1, -1, -1 },
    { 20, -1, -1, -1 },
    { -1, -1, -1, -1 },
};

// directions
//  0:  0.577,  0.577,  0.577
//  1: -0.577,  0.577,  0.577
//  2:  0.000,  0.707,  0.707
//  3: -0.577, -0.577,  0.577
//  4: -0.707,  0.000,  0.707
//  5:  0.000,  0.000,  1.000
//  6:  0.577, -0.577,  0.577
//  7:  0.707,  0.000,  0.707
//  8:  0.000, -0.707,  0.707
//  9:  0.577,  0.577, -0.577
// 10:  0.707,  0.707,  0.000
// 11:  0.000,  1.000,  0.000
// 12: -0.707,  0.707,  0.000
// 13:  1.000,  0.000,  0.000
// 14:  0.707, -0.707,  0.000
// 15: -0.577,  0.577, -0.577
// 16: -1.000,  0.000,  0.000
// 17: -0.707, -0.707,  0.000
// 18:  0.000,  0.707, -0.707
// 19:  0.577, -0.577, -0.577
// 20: -0.577, -0.577, -0.577
// 21:  0.000, -1.000,  0.000
// 22: -0.707,  0.000, -0.707
// 23:  0.000, -0.707, -0.707
// 24:  0.000,  0.000, -1.000
// 25:  0.707,  0.000, -0.707

////////////////////////////////////////////////////////////
// Face hole test data, down side

       /* 128 |7|----------10---------|6| 64
              / |                     / |
             /  |                    /  |
           11   |                   9   |
           /    |                  /    |
          /     7                 /     6
         /      |                /      |
    16 |4|----------8----------|5| 32   |
        |       |               |       |
        |       |               |       |
        |       |               |       |
        |    8 |3|----------2---|------|2| 4
        4      /|               5      /|
        |     / |               |     / |
        |    3  |               |    1  |
        |   /   |               |   /   |
        |  /    19              |  /    18
        | /     |               | /     |
     1 |0|----------0----------|1| 2    |
        |       |               |       |
        |       |               |       |
        |       |               |       |
        | 2048 |11|--------14---|------|10| 1024
        16     /                17     /
        |     /                 |     /
        |    15                 |    13
        |   /                   |   /
        |  /                    |  /
        | /                     | /
   256 |8|----------12---------|9| 512        */

// 2D array for all 256 combinations of cube nodes states for face hole test, 
// have state of down neighbour cube in case of face hole
const int down_hole_code[256][11] = {
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 0
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 1
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 2
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 3
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 4
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 5 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 6
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 7
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 8
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 9
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 10 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 11
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 12
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 13
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 14
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 15
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 16
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 17
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 18
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 19
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 20
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 21 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 22
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 23
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 24
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 25
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 26 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 27
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 28
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 29
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 30
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 31
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 32
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 33
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 34
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 35
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 36
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 37 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 38
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 39
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 40
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 41
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 42 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 43
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 44
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 45
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 46
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 47
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 48
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 49
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 50
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 51
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 52
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 53 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 54
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 55
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 56
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 57
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 58 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 59
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 60
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 61
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 62
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 63
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 64
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 65
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 66
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 67
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 68
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 69 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 70
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 71
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 72
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 73
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 74 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 75
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 76
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 77
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 78
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 79
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 80
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 81
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 82
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 83
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 84
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 85 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 86
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 87
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 88
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 89
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 90 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 91
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 92
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 93
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 94
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 95
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 96
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 97
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 98
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 99
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 100
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 101 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 102
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 103
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 104
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 105
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 106 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 107
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 108
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 109
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 110
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 111
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 112
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 113
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 114
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 115
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 116
    {80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 92},             // 117 o
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 118
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 119
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 120
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 121
    {160, 161, 162, 163, 164, 165, 166, 168, 169, 170, 172},  // 122 o
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 123
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 124
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 125
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 126
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 127
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 128
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 129
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 130
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 131
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 132
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 133 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 134
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 135
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 136
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 137
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 138 i
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 139
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 140
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 141
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 142
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 143
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 144
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 145
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 146
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 147
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 148
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 149
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 150
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 151
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 152
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 153
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 154
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 155
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 156
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 157
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 158
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 159
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 160
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 161
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 162
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 163
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 164
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 165
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 166
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 167
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 168
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 169
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 170
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 171
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 172
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 173
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 174
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 175
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 176
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 177
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 178
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 179
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 180
    {80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 92},             // 181
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 182
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 183
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 184
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 185
    {160, 161, 162, 163, 164, 165, 166, 168, 169, 170, 172},  // 186
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 187
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 188
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 189
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 190
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 191
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 192
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 193
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 194
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 195
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 196
    {87, 91, 93, 94, 95, -1, -1, -1, -1, -1, -1},             // 197
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 198
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 199
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 200
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 201
    {167, 171, 173, 174, 175, -1, -1, -1, -1, -1, -1},        // 202
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 203
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 204
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 205
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 206
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 207
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 208
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 209
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 210
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 211
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 212
    {80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 92},             // 213
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 214
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 215
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 216
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 217
    {160, 161, 162, 163, 164, 165, 166, 168, 169, 170, 172},  // 218
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 219
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 220
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 221
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 222
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 223
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 224
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 225
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 226
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 227
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 228
    {80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 92},             // 229
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 230
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 231
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 232
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 233
    {160, 161, 162, 163, 164, 165, 166, 168, 169, 170, 172},  // 234
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 235
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 236
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 237
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 238
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 239
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 240
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 241
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 242
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 243
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 244
    {80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 92},             // 245
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 246
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 247
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 248
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 249
    {160, 161, 162, 163, 164, 165, 166, 168, 169, 170, 172},  // 250
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 251
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 252
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 253
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 254
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};            // 255

////////////////////////////////////////////////////////////
// Face hole test data, front side

                /* 128 |7|----------10---------|6| 64
                      / |                     / |
                     /  |                    /  |
                   11   |                   9   |
                   /    |                  /    |
                  /     7                 /     6
                 /      |                /      |
            16 |4|----------8----------|5| 32   |
              / |       |             / |       |
             /  |       |            /  |       |
            19  |       |           18  |       |
           /    |    8 |3|----2----/----|------|2| 4
          /     4      /          /     5      /
    1024 /      |     3          /      |     /
       |10|-------------17-----|11|     |    1
        |       |   /           | 2048  |   /
        |       |  /            |       |  /
        |       | /             |       | /
        |    1 |0|----------0---|------|1| 2
        15     /                16     /
        |     /                 |     /
        |    14                 |    13
        |   /                   |   /
        |  /                    |  /
        | /                     | /
   256 |8|----------12---------|9| 512        */

// 2D array for all 256 combinations of cube nodes states for face hole test, 
// have state of front neighbour cube in case of face hole
const int front_hole_code[256][11] = {
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 0
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 1
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 2
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 3
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 4
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 5
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 6
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 7
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 8
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 9
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 10
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 11
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 12
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 13
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 14
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 15
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 16
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 17
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 18
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 19
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 20
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 21
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 22
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 23
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 24
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 25
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 26
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 27
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 28
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 29
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 30
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 31
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 32
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 33
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 34
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 35
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 36
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 37
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 38
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 39
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 40
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 41
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 42
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 43
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 44
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 45
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 46
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 47
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 48
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 49
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 50
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 51
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 52
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 53
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 54
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 55
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 56
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 57
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 58
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 59
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 60
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 61
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 62
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 63
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 64
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 65
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 66
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 67
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 68
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 69
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 70
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 71
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 72
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 73
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 74
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 75
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 76
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 77
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 78
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 79
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 80
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 81
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 82
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 83
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 84
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 85
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 86
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 87
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 88
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 89
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 90
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 91
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 92
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 93
    {132, 133, 134, 135, 148, 149, 150, 164, 165, 166, 180},  // 94
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 95
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 96
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 97
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 98
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 99
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 100
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 101
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 102
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 103
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 104
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 105
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 106
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 107
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 108
    {72, 73, 74, 75, 88, 89, 90, 104, 105, 106, 120},         // 109
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 110
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 111
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 112
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 113
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 114
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 115
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 116
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 117
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 118
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 119
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 120
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 121
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 122
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 123
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 124
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 125
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 126
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 127
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 128
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 129
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 130
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 131
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 132
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 133
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 134
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 135
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 136
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 137
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 138
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 139
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 140
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 141
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 142
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 143
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 144
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 145
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 146
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 147
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 148
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 149
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 150
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 151
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 152
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 153
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 154
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 155
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 156
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 157
    {132, 133, 134, 135, 148, 149, 150, 164, 165, 166, 180},  // 158
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 159
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 160
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 161
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 162
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 163
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 164
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 165
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 166
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 167
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 168
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 169
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 170
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 171
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 172
    {72, 73, 74, 75, 88, 89, 90, 104, 105, 106, 120},         // 173
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 174
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 175
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 176
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 177
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 178
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 179
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 180
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 181
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 182
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 183
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 184
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 185
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 186
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 187
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 188
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 189
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 190
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 191
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 192
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 193
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 194
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 195
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 196
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 197
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 198
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 199
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 200
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 201
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 202
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 203
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 204
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 205
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 206
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 207
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 208
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 209
    {151, 167, 181, 182, 183, -1, -1, -1, -1, -1, -1},        // 210
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 211
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 212
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 213
    {132, 133, 134, 135, 148, 149, 150, 164, 165, 166, 180},  // 214
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 215
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 216
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 217
    {132, 133, 134, 135, 148, 149, 150, 164, 165, 166, 180},  // 218
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 219
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 220
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 221
    {132, 133, 134, 135, 148, 149, 150, 164, 165, 166, 180},  // 222
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 223
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 224
    {91, 107, 121, 122, 123, -1, -1, -1, -1, -1, -1},         // 225
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 226
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 227
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 228
    {72, 73, 74, 75, 88, 89, 90, 104, 105, 106, 120},         // 229
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 230
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 231
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 232
    {72, 73, 74, 75, 88, 89, 90, 104, 105, 106, 120},         // 233
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 234
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 235
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 236
    {72, 73, 74, 75, 88, 89, 90, 104, 105, 106, 120},         // 237
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 238
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 239
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 240
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 241
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 242
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 243
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 244
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 245
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 246
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 247
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 248
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 249
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 250
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 251
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 252
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 253
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 254
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};            // 255

////////////////////////////////////////////////////////////
// Face hole test data, left side

      /* 2048 |11|---------18---------|7|----------10---------|6| 64
              / |                     / | 128                 / |
             /  |                    /  |                    /  |
           19   |                  11   |                   9   |
           /    |                  /    |                  /    |
          /     16                /     7                 /     6
         /      |             16 /      |                /      |
  1024 |10|---------17---------|4|----------8----------|5| 32   |
        |       |               |       |               |       |
        |       |               |       |               |       |
        |       |               |     8 |               |       |
        |  512 |9|----------13--|------|3|----------2---|------|2| 4
        15     /                4      /                5      /
        |     /                 |     /                 |     /
        |    14                 |    3                  |    1
        |   /                   |   /                   |   /
        |  /                    |  /                    |  /
        | /                   1 | /                     | /
   256 |8|----------12---------|0|----------0----------|1| 2        */

// 2D array for all 256 combinations of cube nodes states for face hole test, 
// have state of left neighbour cube in case of face hole
const int left_hole_code[256][11] = { 
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},            // 0
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 1
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 2
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 3
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 4
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 5
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 6
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 7
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 8
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 9
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 10
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 11
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 12
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 13
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 14
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 15
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 16
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 17
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 18
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 19
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 20
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 21
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 22
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 23
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 24
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 25
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 26
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 27
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 28
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 29
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 30
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 31
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 32
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 33
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 34
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 35
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 36
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 37
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 38
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 39
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 40
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 41
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 42
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 43
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 44
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 45
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 46
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 47
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 48
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 49
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 50
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 51
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 52
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 53
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 54
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 55
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 56
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 57
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 58
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 59
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 60
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 61
    {36, 37, 44, 45, 52, 53, 60, 164, 165, 172, 180},         // 62
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 63
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 64
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 65
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 66
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 67
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 68
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 69
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 70
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 71
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 72
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 73
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 74
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 75
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 76
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 77
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 78
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 79
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 80
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 81
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 82
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 83
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 84
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 85
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 86
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 87
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 88
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 89
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 90
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 91
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 92
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 93
    {36, 37, 44, 45, 52, 53, 60, 164, 165, 172, 180},         // 94
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 95
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 96
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 97
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 98
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 99
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 100
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 101
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 102
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 103
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 104
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 105
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 106
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 107
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 108
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 109
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 110
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 111
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 112
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 113
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 114
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 115
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 116
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 117
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 118
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 119
    {61, 173, 181, 188, 189, -1, -1, -1, -1, -1, -1},         // 120
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 121
    {36, 37, 44, 45, 52, 53, 60, 164, 165, 172, 180},         // 122
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 123
    {36, 37, 44, 45, 52, 53, 60, 164, 165, 172, 180},         // 124
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 125
    {36, 37, 44, 45, 52, 53, 60, 164, 165, 172, 180},         // 126
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 127
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 128
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 129
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 130
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 131
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 132
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 133
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 134
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 135
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 136
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 137
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 138
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 139
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 140
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 141
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 142
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 143
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 144
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 145
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 146
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 147
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 148
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 149
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 150
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 151
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 152
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 153
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 154
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 155
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 156
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 157
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 158
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 159
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 160
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 161
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 162
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 163
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 164
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 165
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 166
    {66, 67, 74, 75, 82, 83, 90, 194, 195, 202, 210},         // 167
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 168
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 169
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 170
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 171
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 172
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 173
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 174
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 175
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 176
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 177
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 178
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 179
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 180
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 181
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 182
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 183
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 184
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 185
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 186
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 187
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 188
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 189
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 190
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 191
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 192
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 193
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 194
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 195
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 196
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 197
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 198
    {66, 67, 74, 75, 82, 83, 90, 194, 195, 202, 210},         // 199
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 200
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 201
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 202
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 203
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 204
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 205
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 206
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 207
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 208
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 209
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 210
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 211
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 212
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 213
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 214
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 215
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 216
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 217
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 218
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 219
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 220
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 221
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 222
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 223
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 224
    {91, 203, 211, 218, 219, -1, -1, -1, -1, -1, -1},         // 225
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 226
    {66, 67, 74, 75, 82, 83, 90, 194, 195, 202, 210},         // 227
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 228
    {66, 67, 74, 75, 82, 83, 90, 194, 195, 202, 210},         // 229
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 230
    {66, 67, 74, 75, 82, 83, 90, 194, 195, 202, 210},         // 231
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 232
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 233
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 234
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 235
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 236
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 237
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 238
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 239
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 240
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 241
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 242
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 243
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 244
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 245
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 246
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 247
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 248
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 249
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 250
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 251
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 252
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 253
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},             // 254
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};            // 255

////////////////////////////////////////////////////////////
// 2D array for cube edge indexes 

const int edge_indexes [12][2] = {{0, 1}, {1, 2}, {2, 3}, {0, 3}, {0, 4}, {1, 5}, {2, 6}, {3, 7}, {4, 5}, {5, 6}, {6, 7}, {4, 7}};

const double quality_limit = 0.0;

////////////////////////////////////////////////////////////
//! Result of dot product that is used for normal comparison
const double allowedDot = 0.9999;

CMarchingCubes::CMarchingCubes()
{
    m_mesh = NULL;
}

CMarchingCubes::~CMarchingCubes()
{ }

bool CMarchingCubes::generateMesh(
    geometry::CMesh &mesh,
    const IMarchingCubesFunctor *volumeFunctor,
    bool reduceFlatAreas,
    int numOfIterations,
    bool eliminateNearVertices,
    float maxEdgeLength)
{
    const int desiredCubesOnEdge = 8;
    vpl::img::CSize3i volumeSize = volumeFunctor->getVolumeDimensions();
    VPL_LOG_INFO("Generating mesh from " << volumeSize.x() << "x" << volumeSize.y() << "x" << volumeSize.z());    

    vpl::img::CSize3i cubeSize;
    cubeSize.x() = (volumeSize.x() + desiredCubesOnEdge - 1) / desiredCubesOnEdge;
    cubeSize.y() = (volumeSize.y() + desiredCubesOnEdge - 1) / desiredCubesOnEdge;
    cubeSize.z() = (volumeSize.z() + desiredCubesOnEdge - 1) / desiredCubesOnEdge;

    vpl::img::CVector3i cubesOnEdge;
    cubesOnEdge.x() = volumeSize.x() / cubeSize.x() + 1;
    cubesOnEdge.y() = volumeSize.y() / cubeSize.y() + 1;
    cubesOnEdge.z() = volumeSize.z() / cubeSize.z() + 1;

    const int workerCount = cubesOnEdge.x() * cubesOnEdge.y() * cubesOnEdge.z();
    std::vector<CMarchingCubesWorker> workers(workerCount);

    setProgressMax(workerCount * (reduceFlatAreas ? 3 : 2));
    beginProgress();

    // initialize workers
    for (int z = 0; z < cubesOnEdge.z(); ++z)
    {
        for (int y = 0; y < cubesOnEdge.y(); ++y)
        {
            for (int x = 0; x < cubesOnEdge.x(); ++x)
            {
                int index = z * cubesOnEdge.y() * cubesOnEdge.x() + y * cubesOnEdge.x() + x;
                CMarchingCubesWorker &worker = workers[index];
                worker.setVolumeOfInterest(
                    std::min(x * cubeSize.x(), volumeSize.x()),
                    std::min(y * cubeSize.y(), volumeSize.y()),
                    std::min(z * cubeSize.z(), volumeSize.z()),
                    std::min((x + 1) * cubeSize.x(), volumeSize.x() + 1),
                    std::min((y + 1) * cubeSize.y(), volumeSize.y() + 1),
                    std::min((z + 1) * cubeSize.z(), volumeSize.z() + 1));
            }
        }
    }

    bool earlyBreak = false;

    // generate submeshes
    #pragma omp parallel for schedule(dynamic)
    for (int i = 0; i < workerCount; ++i)
    {
        if (earlyBreak)
        {
            continue;
        }
        //auto start = std::chrono::high_resolution_clock::now();

        workers[i].generateMesh(volumeFunctor, reduceFlatAreas);

        //auto dur = std::chrono::high_resolution_clock::now() - start;
        //auto seconds = std::chrono::duration_cast<std::chrono::seconds>(dur);
        //auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(dur) - seconds;
        //VPL_LOG_INFO("Marching cubes worker " << i << " finished in " << seconds.count() << ":" << milli.count() << " on thread " << omp_get_thread_num());

        #pragma omp critical
        earlyBreak = !progress();
    }
    if (earlyBreak)
    {
        endProgress();
        return false;
    }

    // generate submeshes
    if (reduceFlatAreas)
    {
        #pragma omp parallel for schedule(dynamic)
        for (int i = 0; i < workerCount; ++i)
        {
            if (earlyBreak)
            {
                continue;
            }

            // and reduce flat areas (and improve quality) if desired
            workers[i].reduceFlatAreas(numOfIterations, eliminateNearVertices, maxEdgeLength);

            #pragma omp critical
            earlyBreak = !progress();
        }
    }
    if (earlyBreak)
    {
        endProgress();
        return false;
    }

    //auto start = std::chrono::high_resolution_clock::now();

    // merge submeshes
    mesh.clear();
    std::map<int, std::set<std::pair<geometry::CMesh::VertexHandle, int> > > vertexMap;
    std::vector<geometry::CMesh::VertexHandle> seamVertices;
    for (int i = 0; i < workerCount; ++i)
    {
        mergeMeshes(workers[i].getMesh(), workers[i].getOpenEdgeVertices(), mesh, vertexMap, seamVertices);

        if (!progress())
        {
            endProgress();
            return false;
        }
    }

    //auto dur = std::chrono::high_resolution_clock::now() - start;
    //auto seconds = std::chrono::duration_cast<std::chrono::seconds>(dur);
    //auto milli = std::chrono::duration_cast<std::chrono::milliseconds>(dur) - seconds;
    //VPL_LOG_INFO("Marching cubes meshes merged in " << seconds.count() << ":" << milli.count());

    mesh.garbage_collection();

	if (reduceFlatAreas && mesh.n_vertices()>0)
    {
        CMarchingCubesWorker::reduceFlatAreas(mesh, seamVertices, numOfIterations, eliminateNearVertices, maxEdgeLength);

        OpenMesh::FPropHandleT<short> fProp_flag;
        if (mesh.get_property_handle(fProp_flag, "fProp_flag"))
        {
            mesh.remove_property(fProp_flag);
        }

        OpenMesh::VPropHandleT<short> vProp_flag;
        if (mesh.get_property_handle(vProp_flag, "vProp_flag"))
        {
            mesh.remove_property(vProp_flag);
        }
    }

    endProgress();

    bool isTwoManifold = mesh.isTwoManifold();
    assert(isTwoManifold);
    assert(mesh.isClosed());

#if(0)
    int boundaryEdgesCount = 0;
    std::vector<std::pair<geometry::CMesh::Point, geometry::CMesh::VertexHandle> > boundaryVertices;
    for (geometry::CMesh::EdgeIter eit = mesh.edges_begin(); eit != mesh.edges_end(); ++eit)
    {
        if (mesh.is_boundary(eit.handle()))
        {
            geometry::CMesh::HalfedgeHandle hedge = mesh.halfedge_handle(eit.handle(), 0);
            if (!hedge.is_valid())
            {
                hedge = mesh.halfedge_handle(eit.handle(), 1);
            }
            geometry::CMesh::VertexHandle vh[2];
            geometry::CMesh::Point p[2];

            vh[0] = mesh.from_vertex_handle(hedge);
            vh[1] = mesh.to_vertex_handle(hedge);
            p[0] = mesh.point(vh[0]);
            p[1] = mesh.point(vh[1]);
            boundaryVertices.push_back(std::pair<geometry::CMesh::Point, geometry::CMesh::VertexHandle>(p[0], vh[0]));
            boundaryVertices.push_back(std::pair<geometry::CMesh::Point, geometry::CMesh::VertexHandle>(p[1], vh[1]));

            boundaryEdgesCount++;
        }
    }
#endif

    return true;
}

void CMarchingCubes::mergeMeshes(geometry::CMesh &sourceMesh, std::set<std::pair<geometry::CMesh::VertexHandle, int> > &openEdgeVertices, geometry::CMesh &targetMesh, std::map<int, std::set<std::pair<geometry::CMesh::VertexHandle, int> > > &vertexMap, std::vector<geometry::CMesh::VertexHandle> &seamVertices)
{
    if (sourceMesh.n_vertices() == 0)
    {
        return;
    }

    OpenMesh::VPropHandleT<geometry::CMesh::VertexHandle> vProp_targetHandle;
    sourceMesh.add_property(vProp_targetHandle, "vProp_targetHandle");

    // handle edge vertices first and try to find them in target mesh
    for (std::set<std::pair<geometry::CMesh::VertexHandle, int> >::iterator oevit = openEdgeVertices.begin(); oevit != openEdgeVertices.end(); ++oevit)
    {
        geometry::CMesh::Point sourcePoint = sourceMesh.point(oevit->first);
        int hashValue = int(sourcePoint[2]) / 10 * 1000000 + int(sourcePoint[1]) / 10 * 1000 + int(sourcePoint[0]) / 10 * 1;
        std::set<std::pair<geometry::CMesh::VertexHandle, int> > &hashVertexMap = vertexMap[hashValue];

        std::set<std::pair<geometry::CMesh::VertexHandle, int> >::iterator foundVertex = hashVertexMap.end();
        for (std::set<std::pair<geometry::CMesh::VertexHandle, int> >::iterator hvit = hashVertexMap.begin(); hvit != hashVertexMap.end(); ++hvit)
        {
            if ((sourcePoint - targetMesh.point(hvit->first)).length() < 0.001)
            {
                foundVertex = hvit;
                break;
            }
        }

        if (foundVertex == hashVertexMap.end())
        {
            geometry::CMesh::VertexHandle newVertex = targetMesh.add_vertex(sourcePoint);
            sourceMesh.property(vProp_targetHandle, oevit->first) = newVertex;
            hashVertexMap.insert(std::pair<geometry::CMesh::VertexHandle, int>(newVertex, oevit->second - 1));
            seamVertices.push_back(newVertex);
        }
        else
        {
            sourceMesh.property(vProp_targetHandle, oevit->first) = foundVertex->first;

            geometry::CMesh::VertexHandle vertexHandle = foundVertex->first;
            int usages = foundVertex->second - 1;

            hashVertexMap.erase(foundVertex);
            if (usages != 0)
            {
                hashVertexMap.insert(std::pair<geometry::CMesh::VertexHandle, int>(vertexHandle, usages));
            }
        }
    }

    // handle all other vertices
    for (geometry::CMesh::VertexIter vit = sourceMesh.vertices_begin(); vit != sourceMesh.vertices_end(); ++vit)
    {
        if (sourceMesh.property(vProp_targetHandle, vit.handle()).is_valid())
        {
            continue;
        }

        geometry::CMesh::VertexHandle newVertex = targetMesh.add_vertex(sourceMesh.point(vit.handle()));
        sourceMesh.property(vProp_targetHandle, vit.handle()) = newVertex;
    }

    OpenMesh::FPropHandleT<short> src_fProp_flag;
    OpenMesh::FPropHandleT<short> dst_fProp_flag;
    bool flagIsPresent = sourceMesh.get_property_handle(src_fProp_flag, "fProp_flag");
    if (flagIsPresent)
    {
        if (!targetMesh.get_property_handle(dst_fProp_flag, "fProp_flag"))
        {
            targetMesh.add_property(dst_fProp_flag, "fProp_flag");
        }
    }

    // copy faces using handle map
    for (geometry::CMesh::FaceIter fit = sourceMesh.faces_begin(); fit != sourceMesh.faces_end(); ++fit)
    {
        geometry::CMesh::FaceHandle faceHandle = fit.handle();
        std::vector<geometry::CMesh::VertexHandle> vertexHandles;
        for (geometry::CMesh::FaceVertexIter fvit = sourceMesh.fv_begin(faceHandle); fvit != sourceMesh.fv_end(faceHandle); ++fvit)
        {
            vertexHandles.push_back(sourceMesh.property(vProp_targetHandle, fvit.handle()));
        }

        assert(vertexHandles.size() == 3);

        geometry::CMesh::FaceHandle newFace = targetMesh.add_face(vertexHandles[0], vertexHandles[1], vertexHandles[2]);

        if (flagIsPresent)
        {
            targetMesh.property(dst_fProp_flag, newFace) = sourceMesh.property(src_fProp_flag, faceHandle);
        }
    }

    // clean
    sourceMesh.remove_property(vProp_targetHandle);
}

CMarchingCubesWorker::CMarchingCubesWorker()
{
    // initialization of work matrices to NULL value
    m_state_matrix_up = NULL;
    m_state_matrix_down = NULL;
    m_node_matrix_up_h = NULL;
    m_node_matrix_up_v = NULL;
    m_node_matrix_down_h = NULL;
    m_node_matrix_down_v = NULL;
    m_node_matrix_middle = NULL;
    m_cube_code_matrix = NULL;
    m_down_cube_code_matrix = NULL;
}

CMarchingCubesWorker::~CMarchingCubesWorker()
{
    // deallocation of work matrices
    deallocWorktMem();
}

geometry::CMesh &CMarchingCubesWorker::getMesh()
{
    return m_mesh;
}

std::set<std::pair<geometry::CMesh::VertexHandle, int> > &CMarchingCubesWorker::getOpenEdgeVertices()
{
    return m_openEdgeVertices;
}

void CMarchingCubesWorker::setVolumeOfInterest(vpl::tSize startX, vpl::tSize startY, vpl::tSize startZ, vpl::tSize endX, vpl::tSize endY, vpl::tSize endZ)
{
    m_volume_start_x = startX;
    m_volume_start_y = startY;
    m_volume_start_z = startZ;
    m_volume_end_x = endX;
    m_volume_end_y = endY;
    m_volume_end_z = endZ;
}

void CMarchingCubesWorker::setCodeCoordinateZ(double z, double dz)
{ 
    m_cube_vertices[0][2] = float(z);
    m_cube_vertices[1][2] = m_cube_vertices[0][2];
    m_cube_vertices[2][2] = m_cube_vertices[0][2];
    m_cube_vertices[3][2] = m_cube_vertices[0][2];
    m_cube_vertices[4][2] = m_cube_vertices[0][2] + float(dz);
    m_cube_vertices[5][2] = m_cube_vertices[4][2];
    m_cube_vertices[6][2] = m_cube_vertices[4][2];
    m_cube_vertices[7][2] = m_cube_vertices[4][2];
}

//! Set cube node Y coordinates.
void CMarchingCubesWorker::setCodeCoordinateY(double y, double dy)
{
    m_cube_vertices[0][1] = float(y);
    m_cube_vertices[1][1] = m_cube_vertices[0][1];
    m_cube_vertices[2][1] = m_cube_vertices[0][1] + float(dy);
    m_cube_vertices[3][1] = m_cube_vertices[2][1];
    m_cube_vertices[4][1] = m_cube_vertices[0][1];
    m_cube_vertices[5][1] = m_cube_vertices[0][1];
    m_cube_vertices[6][1] = m_cube_vertices[2][1];
    m_cube_vertices[7][1] = m_cube_vertices[2][1];
}

//! Set cube node X coordinates.
void CMarchingCubesWorker::setCodeCoordinateX(double x, double dx)
{ 
    m_cube_vertices[0][0] = float(x);
    m_cube_vertices[1][0] = m_cube_vertices[0][0] + float(dx);
    m_cube_vertices[2][0] = m_cube_vertices[1][0];
    m_cube_vertices[3][0] = m_cube_vertices[0][0];
    m_cube_vertices[4][0] = m_cube_vertices[0][0];
    m_cube_vertices[5][0] = m_cube_vertices[1][0];
    m_cube_vertices[6][0] = m_cube_vertices[1][0];
    m_cube_vertices[7][0] = m_cube_vertices[0][0];
}

//! Get cube code of down cube.
unsigned char CMarchingCubesWorker::getCubeCodeDown(int x, int y)
{
    return m_down_cube_code_matrix[(y + 1) * m_work_matrices_size_x + x + 1];
}

//! Get cube code of front cube.
unsigned char CMarchingCubesWorker::getCubeCodeFront(int x, int y) 
{ 
    return m_cube_code_matrix[y * m_work_matrices_size_x + x + 1];
}

//! Get cube code of left cube.
unsigned char CMarchingCubesWorker::getCubeCodeLeft(int x, int y)
{
    return m_cube_code_matrix[(y + 1) * m_work_matrices_size_x + x];
}

//! Create actual cube code and save it into code matrix
unsigned char CMarchingCubesWorker::makeCubeCode(int x, int y)
{
    // create cube code
    unsigned char code = 0;
    code += m_state_matrix_down[y * m_work_matrices_size_x + x];
    code += m_state_matrix_down[y * m_work_matrices_size_x + x + 1] << 1;
    code += m_state_matrix_down[((y + 1) * m_work_matrices_size_x) + x + 1] << 2;
    code += m_state_matrix_down[((y + 1) * m_work_matrices_size_x) + x] << 3;
    code += m_state_matrix_up[y * m_work_matrices_size_x + x] << 4;
    code += m_state_matrix_up[y * m_work_matrices_size_x + x + 1] << 5;
    code += m_state_matrix_up[((y + 1) * m_work_matrices_size_x) + x + 1] << 6;
    code += m_state_matrix_up[((y + 1) * m_work_matrices_size_x) + x] << 7;

    // save cube code into code matrix
    m_cube_code_matrix[(y + 1) * m_work_matrices_size_x + x + 1] = code;

    return code;
}

//! Calculate number of nodes for cube code.
int CMarchingCubesWorker::cubeCodeNodeNumber(unsigned char cube_code)
{
    int voxels = cube_code & 1;
    voxels += (cube_code >> 1) & 1;
    voxels += (cube_code >> 2) & 1;
    voxels += (cube_code >> 3) & 1;
    voxels += (cube_code >> 4) & 1;
    voxels += (cube_code >> 5) & 1;
    voxels += (cube_code >> 6) & 1;
    voxels += (cube_code >> 7) & 1;
    return voxels;
}

bool CMarchingCubesWorker::generateMesh(const IMarchingCubesFunctor *volumeFunctor, bool markFaces)
{
    m_voxelSize = volumeFunctor->getVoxelSize();
    const vpl::img::CSize3i fullvolumeSize = volumeFunctor->getVolumeDimensions();

    // save pointer on actual tri mesh
    m_mesh.clear();
    m_mesh.garbage_collection();

    if (markFaces)
    {
        OpenMesh::FPropHandleT<short> fProp_flag;
        m_mesh.add_property(fProp_flag, "fProp_flag");
    }

    vpl::tSize volume_size_x = m_volume_end_x - m_volume_start_x;
    vpl::tSize volume_size_y = m_volume_end_y - m_volume_start_y;
    vpl::tSize volume_size_z = m_volume_end_z - m_volume_start_z;

    // allocation of work matrices
    allocWorktMem(volume_size_x, volume_size_y);

    // cycle through volume
    for (int z = -1; z <= volume_size_z; ++z)
    {
        if ((z < 0) && (m_volume_start_z == 0))
        {
            continue;
        }

        setCodeCoordinateZ((z + m_volume_start_z - 0.5) * m_voxelSize.z(), m_voxelSize.z());

        for (int y = -1; y <= volume_size_y; ++y)
        {
            if ((y < 0) && (m_volume_start_y == 0))
            {
                continue;
            }

            for (int x = -1; x <= volume_size_x; ++x)
            {
                if ((x < 0) && (m_volume_start_x == 0))
                {
                    continue;
                }

                // get volume voxel state and save it into up voxel state matrix
                const int idx = ((y + 1) * m_work_matrices_size_x) + x + 1;
                //assert(idx >= 0 && idx < m_work_matrices_size_x * m_work_matrices_size_y);
                m_state_matrix_up[idx] = volumeFunctor->operator()(x + m_volume_start_x, y + m_volume_start_y, z + m_volume_start_z);
#if defined(_DEBUG) || defined(QT_DEBUG) || defined(DEBUG) || defined(NNDEBUG)
                // check functor that it doesn't return non zero values for volume outside - if it does, fix your functor!
                if (x + m_volume_start_x < 0 || x + m_volume_start_x >= fullvolumeSize.x() ||
                    y + m_volume_start_y < 0 || y + m_volume_start_y >= fullvolumeSize.y() || 
                    z + m_volume_start_z < 0 || z + m_volume_start_z >= fullvolumeSize.z())
                {
                    assert(0 == m_state_matrix_up[idx]);
                    m_state_matrix_up[idx] = 0;
                }
#endif
            }
        }

        if (z != -1)
        {
            for (int y = 0; y <= volume_size_y; ++y)
            {
                setCodeCoordinateY((y + m_volume_start_y - 0.5) * m_voxelSize.y(), m_voxelSize.y());

                for (int x = 0; x <= volume_size_x; ++x)
                {
                    setCodeCoordinateX((x + m_volume_start_x - 0.5) * m_voxelSize.x(), m_voxelSize.x());

                    // code of actual cube
                    unsigned char cube_code = makeCubeCode(x, y);

                    // ignore no cross surface cubes
                    if ((cube_code != 0) && (cube_code != 255))
                    {
                        // do this only when really inside volume of interest
                        if ((z < volume_size_z) && (y < volume_size_y) && (x < volume_size_x))
                        {
                            makeTri(x, y, cube_code);
                        }
                        holeFilling(x, y, cube_code, ((y < volume_size_y) && (x < volume_size_x)), ((z < volume_size_z) && (x < volume_size_x)), ((z < volume_size_z) && (y < volume_size_y)));
                    }
                }
            }
        }

        // switch up and down state matrices
        unsigned char *state_matrix_ptr = m_state_matrix_down;
        m_state_matrix_down = m_state_matrix_up;
        m_state_matrix_up = state_matrix_ptr;

        // switch up and down node matrices
        geometry::CMesh::VertexHandle *node_matrix_ptr = m_node_matrix_down_h;
        m_node_matrix_down_h = m_node_matrix_up_h;
        m_node_matrix_up_h = node_matrix_ptr;

        node_matrix_ptr = m_node_matrix_down_v;
        m_node_matrix_down_v = m_node_matrix_up_v;
        m_node_matrix_up_v = node_matrix_ptr;

        // switch cube code matrix with down cube code matrix
        unsigned char *cube_code_matrix_ptr = m_cube_code_matrix;
        m_cube_code_matrix = m_down_cube_code_matrix;
        m_down_cube_code_matrix = cube_code_matrix_ptr;

        // clearing state, node and code matrices
        memset(m_state_matrix_up, 0, m_work_matrices_size_x * m_work_matrices_size_y * sizeof(unsigned char));
        memset(m_cube_code_matrix, 0, m_work_matrices_size_x * m_work_matrices_size_y * sizeof(unsigned char));

        for (int i = 0; i < m_work_matrices_size_x * m_work_matrices_size_y; i++)
        {
            m_node_matrix_middle[i].invalidate();
            m_node_matrix_up_h[i].invalidate();
            m_node_matrix_up_v[i].invalidate();
        }
    }

    // deallocation of work matrices
    deallocWorktMem();

    // delete possible entities marked for deletion
    m_mesh.garbage_collection();
    updateOpenEdgeVertices();

    return true;
}

int ipow(int a, int b)
{
    int retval = 1;
    while (b > 0)
    {
        retval *= a;
        b--;
    }
    return retval;
}

void CMarchingCubesWorker::updateOpenEdgeVertices()
{
    vpl::img::CSize3d start;
    vpl::img::CSize3d end;
    vpl::img::CSize3d limit;

    start.x() = (m_volume_start_x - 0.5) * m_voxelSize.x();
    start.y() = (m_volume_start_y - 0.5) * m_voxelSize.y();
    start.z() = (m_volume_start_z - 0.5) * m_voxelSize.z();

    end.x() = (m_volume_end_x - 0.5) * m_voxelSize.x();
    end.y() = (m_volume_end_y - 0.5) * m_voxelSize.y();
    end.z() = (m_volume_end_z - 0.5) * m_voxelSize.z();

    limit.x() = m_voxelSize.x() * 0.5 * 0.1;
    limit.y() = m_voxelSize.y() * 0.5 * 0.1;
    limit.z() = m_voxelSize.z() * 0.5 * 0.1;

    m_openEdgeVertices.clear();
    for (geometry::CMesh::EdgeIter eit = m_mesh.edges_begin(); eit != m_mesh.edges_end(); ++eit)
    {
        if (m_mesh.is_boundary(eit.handle()))
        {
            geometry::CMesh::HalfedgeHandle halfedge = m_mesh.halfedge_handle(eit.handle(), 0);
            if (!halfedge.is_valid())
            {
                halfedge = m_mesh.halfedge_handle(eit.handle(), 1);
            }

            geometry::CMesh::VertexHandle vh[2];
            vh[0] = m_mesh.from_vertex_handle(halfedge);
            vh[1] = m_mesh.to_vertex_handle(halfedge);

            geometry::CMesh::Point p[2];
            for (int i = 0; i < 2; ++i)
            {
                p[i] = m_mesh.point(vh[i]);

                int usages = 0;
                if (std::abs(p[i][0] - start.x()) < limit.x())
                {
                    usages++;
                }
                if (std::abs(p[i][0] - end.x()) < limit.x())
                {
                    usages++;
                }
                if (std::abs(p[i][1] - start.y()) < limit.y())
                {
                    usages++;
                }
                if (std::abs(p[i][1] - end.y()) < limit.y())
                {
                    usages++;
                }
                if (std::abs(p[i][2] - start.z()) < limit.z())
                {
                    usages++;
                }
                if (std::abs(p[i][2] - end.z()) < limit.z())
                {
                    usages++;
                }

                usages = ipow(2, usages);
                m_openEdgeVertices.insert(std::pair<geometry::CMesh::VertexHandle, int>(vh[i], usages));
            }
        }
    }
}

void CMarchingCubesWorker::allocWorktMem(int size_x, int size_y)
{
    // free allocated work matrices memory
    deallocWorktMem();

    // set work matrices size
    m_work_matrices_size_x = size_x + 10;
    m_work_matrices_size_y = size_y + 10;

    // allocation of work matrices
    int workMatrixSize = m_work_matrices_size_x * m_work_matrices_size_y;
    m_state_matrix_up = new unsigned char[workMatrixSize];
    m_state_matrix_down = new unsigned char[workMatrixSize];
    m_node_matrix_up_h = new geometry::CMesh::VertexHandle[workMatrixSize];
    m_node_matrix_up_v = new geometry::CMesh::VertexHandle[workMatrixSize];
    m_node_matrix_down_h = new geometry::CMesh::VertexHandle[workMatrixSize];
    m_node_matrix_down_v = new geometry::CMesh::VertexHandle[workMatrixSize];
    m_node_matrix_middle = new geometry::CMesh::VertexHandle[workMatrixSize];
    m_cube_code_matrix = new unsigned char[workMatrixSize];
    m_down_cube_code_matrix = new unsigned char[workMatrixSize];

    // clearing work matrices
    memset(m_state_matrix_up, 0, workMatrixSize * sizeof(unsigned char));
    memset(m_state_matrix_down, 0, workMatrixSize * sizeof(unsigned char));
    memset(m_cube_code_matrix, 0, workMatrixSize * sizeof(unsigned char));
    memset(m_down_cube_code_matrix, 0, workMatrixSize * sizeof(unsigned char));
}

void CMarchingCubesWorker::deallocWorktMem()
{
    // free allocated work matrices memory
    delete[] m_state_matrix_up;
    delete[] m_state_matrix_down;
    delete[] m_node_matrix_up_h;
    delete[] m_node_matrix_up_v;
    delete[] m_node_matrix_down_h;
    delete[] m_node_matrix_down_v;
    delete[] m_node_matrix_middle;
    delete[] m_cube_code_matrix;
    delete[] m_down_cube_code_matrix;

    // initialization of work matrices to NULL value
    m_state_matrix_up = NULL;
    m_state_matrix_down = NULL;
    m_node_matrix_up_h = NULL;
    m_node_matrix_up_v = NULL;
    m_node_matrix_down_h = NULL;
    m_node_matrix_down_v = NULL;
    m_node_matrix_middle = NULL;
    m_cube_code_matrix = NULL;
    m_down_cube_code_matrix = NULL;
}

void CMarchingCubesWorker::makeTri(int x, int y, unsigned char cube_code)
{
    OpenMesh::FPropHandleT<short> fProp_flag;
    bool markFaces = m_mesh.get_property_handle(fProp_flag, "fProp_flag");

    geometry::CMesh::Point edge_center;
    geometry::CMesh::VertexHandle tri_vertex[3];

    // cycle of tri
    for (int i = 0; i < 4; i++)
    {
        // test of tri existence for actual cube code
        if (node_state[cube_code][3 * i] == -1)
        {
            return;
        }

        // cycle of edge centers vertices for particular tri creating
        for (int h = 0; h < 3; h++)
        {
            // get edge centers vertices for particular tri creating
            tri_vertex[h] = getCubeEdgeNode(node_state[cube_code][3 * i + h], x, y);
            if (!tri_vertex[h].is_valid())
            {
                // edge center calculation
                int edgeIndex = node_state[cube_code][3 * i + h];
                geometry::CMesh::Point &edgeVertexA = m_cube_vertices[edge_indexes[edgeIndex][0]];
                geometry::CMesh::Point &edgeVertexB = m_cube_vertices[edge_indexes[edgeIndex][1]];
                edge_center = (edgeVertexA + edgeVertexB) / 2.0;

                // create edge center vertex
                tri_vertex[h] = m_mesh.add_vertex(edge_center);
                
                // include new vertex into context data matrix
                setCubeEdgeNode(node_state[cube_code][3 * i + h], x, y, tri_vertex[h]);
            }
        }

        // create new tri
        geometry::CMesh::FaceHandle face = m_mesh.add_face(tri_vertex[0], tri_vertex[1], tri_vertex[2]);
        if (markFaces)
        {
            m_mesh.property(fProp_flag, face) = tri_direction_group[cube_code][i];
        }
    }
}

void CMarchingCubesWorker::holeFilling(int x, int y, unsigned char cube_code, bool fill_down, bool fill_front, bool fill_left)
{
    OpenMesh::FPropHandleT<short> fProp_flag;
    bool markFaces = m_mesh.get_property_handle(fProp_flag, "fProp_flag");

    //unsigned char down_code = GetCubeCodeDown(x, y); // code of down cube 
    //unsigned char front_code = GetCubeCodeFront(x, y); // code of front cube
    unsigned char left_code = getCubeCodeLeft(x, y); // code of left cube

    geometry::CMesh::FaceHandle face;
    geometry::CMesh::Point normal;

    if (fill_down)
    {
        // cycle for hole on cube down face
        for (int i = 0; i < 11; i++)
        {
            // test existence of hole code for down cube
            if (down_hole_code[cube_code][i] == -1)
            {
                break;
            }

            // test unity of down cube hole code with down cube code => hole existence
            else if (down_hole_code[cube_code][i] == getCubeCodeDown(x, y))
            {
                geometry::CMesh::VertexHandle hole_vertex[4]; // array of pointers of hole vertices

                // take hole vertices pointers
                hole_vertex[0] = getCubeEdgeNode(0, x, y);
                hole_vertex[1] = getCubeEdgeNode(1, x, y);
                hole_vertex[2] = getCubeEdgeNode(2, x, y);
                hole_vertex[3] = getCubeEdgeNode(3, x, y);

                // test of cube code node number
                if (cubeCodeNodeNumber(cube_code) > 4)
                {
                    // normal 0 0 -1
                    // created inverted tri to fill hole
                    face = m_mesh.add_face(hole_vertex[0], hole_vertex[2], hole_vertex[1]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 24;
                    }
                    face = m_mesh.add_face(hole_vertex[2], hole_vertex[0], hole_vertex[3]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 24;
                    }
                }
                else
                {
                    // normal 0 0 1
                    // created tri to fill hole
                    face = m_mesh.add_face(hole_vertex[0], hole_vertex[1], hole_vertex[2]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 5;
                    }
                    face = m_mesh.add_face(hole_vertex[2], hole_vertex[3], hole_vertex[0]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 5;
                    }
                }

                break;
            }
        }
    }

    if (fill_front)
    {
        // cycle for hole on cube front face
        for (int i = 0; i < 11; i++)
        {
            // test existence of hole code for front cube
            if (front_hole_code[cube_code][i] == -1)
                break;

            // test unity of front cube hole code with front cube code => hole existence
            else if (front_hole_code[cube_code][i] == getCubeCodeFront(x, y))
            {
                geometry::CMesh::VertexHandle hole_vertex[4]; // array of pointers of hole vertices

                // take hole vertices pointers
                hole_vertex[0] = getCubeEdgeNode(0, x, y);
                hole_vertex[1] = getCubeEdgeNode(5, x, y);
                hole_vertex[2] = getCubeEdgeNode(8, x, y);
                hole_vertex[3] = getCubeEdgeNode(4, x, y);

                // test of cube code node number
                if (cubeCodeNodeNumber(cube_code) > 4)
                {
                    // normal 0 -1 0
                    // created inverted tri to fill hole
                    face = m_mesh.add_face(hole_vertex[0], hole_vertex[2], hole_vertex[3]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 21;
                    }
                    face = m_mesh.add_face(hole_vertex[2], hole_vertex[0], hole_vertex[1]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 21;
                    }
                }
                else
                {
                    // normal 0 1 0
                    // created inverted tri to fill hole
                    face = m_mesh.add_face(hole_vertex[0], hole_vertex[3], hole_vertex[2]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 11;
                    }
                    face = m_mesh.add_face(hole_vertex[2], hole_vertex[1], hole_vertex[0]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 11;
                    }
                }

                break;
            }
        }
    }

    if (fill_left)
    {
        // cycle for hole on cube left face
        for (int i = 0; i < 11; i++)
        {
            // test existence of hole code for left cube
            if (left_hole_code[cube_code][i] == -1)
                break;

            // test unity of left cube hole code with left cube code => hole existence
            else if (left_hole_code[cube_code][i] == left_code)
            {
                geometry::CMesh::VertexHandle hole_vertex[4]; // array of pointers of hole vertices

                // take hole vertices pointers
                hole_vertex[0] = getCubeEdgeNode(3, x, y);
                hole_vertex[1] = getCubeEdgeNode(7, x, y);
                hole_vertex[2] = getCubeEdgeNode(11, x, y);
                hole_vertex[3] = getCubeEdgeNode(4, x, y);

                // test of cube code node number
                if (cubeCodeNodeNumber(cube_code) > 4)
                {
                    // normal -1 0 0
                    // created inverted tri to fill hole
                    face = m_mesh.add_face(hole_vertex[0], hole_vertex[2], hole_vertex[1]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 16;
                    }
                    face = m_mesh.add_face(hole_vertex[2], hole_vertex[0], hole_vertex[3]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 16;
                    }
                }
                else
                {
                    // normal 1 0 0
                    // created inverted tri to fill hole
                    face = m_mesh.add_face(hole_vertex[0], hole_vertex[1], hole_vertex[2]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 13;
                    }
                    face = m_mesh.add_face(hole_vertex[2], hole_vertex[3], hole_vertex[0]);
                    if (markFaces)
                    {
                        m_mesh.property(fProp_flag, face) = 13;
                    }
                }

                break;
            }
        }
    }
}

geometry::CMesh::VertexHandle CMarchingCubesWorker::getCubeEdgeNode(int edge_index, int x, int y)
{
    geometry::CMesh::VertexHandle vh;
    vh.invalidate();
    
    switch (edge_index)
    {
    case 0:
        vh = m_node_matrix_down_h[y * m_work_matrices_size_x + x];
        break;

    case 1:
        vh = m_node_matrix_down_v[y * m_work_matrices_size_x + x + 1];
        break;

    case 2:
        vh = m_node_matrix_down_h[(y + 1) * m_work_matrices_size_x + x];
        break;

    case 3:
        vh = m_node_matrix_down_v[y * m_work_matrices_size_x + x];
        break;

    case 4:
        vh = m_node_matrix_middle[y * m_work_matrices_size_x + x];
        break;

    case 5:
        vh = m_node_matrix_middle[y * m_work_matrices_size_x + x + 1];
        break;

    case 6:
        vh = m_node_matrix_middle[(y + 1) * m_work_matrices_size_x + x + 1];
        break;

    case 7:
        vh = m_node_matrix_middle[(y + 1) * m_work_matrices_size_x + x];
        break;

    case 8:
        vh = m_node_matrix_up_h[y * m_work_matrices_size_x + x];
        break;

    case 9:
        vh = m_node_matrix_up_v[y * m_work_matrices_size_x + x + 1];
        break;

    case 10:
        vh = m_node_matrix_up_h[(y + 1) * m_work_matrices_size_x + x];
        break;

    case 11:
        vh = m_node_matrix_up_v[y * m_work_matrices_size_x + x];
        break;
    }

    return vh;
}

void CMarchingCubesWorker::setCubeEdgeNode(int edge_index, int x, int y, geometry::CMesh::VertexHandle new_vertex)
{
    switch (edge_index)
    {
    case 0:
        m_node_matrix_down_h[y * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 1:
        m_node_matrix_down_v[y * m_work_matrices_size_x + x + 1] = new_vertex;
        break;

    case 2:
        m_node_matrix_down_h[(y + 1) * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 3:
        m_node_matrix_down_v[y * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 4:
        m_node_matrix_middle[y * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 5:
        m_node_matrix_middle[y * m_work_matrices_size_x + x + 1] = new_vertex;
        break;

    case 6:
        m_node_matrix_middle[(y + 1) * m_work_matrices_size_x + x + 1] = new_vertex;
        break;

    case 7:
        m_node_matrix_middle[(y + 1) * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 8:
        m_node_matrix_up_h[y * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 9:
        m_node_matrix_up_v[y * m_work_matrices_size_x + x + 1] = new_vertex;
        break;

    case 10:
        m_node_matrix_up_h[(y + 1) * m_work_matrices_size_x + x] = new_vertex;
        break;

    case 11:
        m_node_matrix_up_v[y * m_work_matrices_size_x + x] = new_vertex;
        break;
    }
}

void CMarchingCubesWorker::markupVertices(geometry::CMesh &mesh, std::vector<geometry::CMesh::VertexHandle> &vertices)
{
    OpenMesh::FPropHandleT<short> fProp_flag;
    mesh.get_property_handle(fProp_flag, "fProp_flag");
    assert(fProp_flag.is_valid());

    // add flag property
    OpenMesh::VPropHandleT<int> vProp_flag;
    mesh.add_property(vProp_flag, "vProp_flag");

    // calculate normals
    mesh.update_face_normals();

    // calculate vertex flags
    if (vertices.size() == 0)
    {
        for (geometry::CMesh::VertexIter vit = mesh.vertices_begin(); vit != mesh.vertices_end(); ++vit)
        {
            resolveVertexFlag(mesh, vit.handle(), vProp_flag, fProp_flag);
        }
    }
    else
    {
        for (std::vector<geometry::CMesh::VertexHandle>::iterator vit = vertices.begin(); vit != vertices.end(); ++vit)
        {
            resolveVertexFlag(mesh, *vit, vProp_flag, fProp_flag);
        }
    }

    // adjust flags to resolve near vertices
    if (vertices.size() == 0)
    {
        for (geometry::CMesh::VertexIter vit = mesh.vertices_begin(); vit != mesh.vertices_end(); ++vit)
        {
            resolveNearVertexFlag(mesh, vit.handle(), vProp_flag);
        }
    }
    else
    {
        for (std::vector<geometry::CMesh::VertexHandle>::iterator vit = vertices.begin(); vit != vertices.end(); ++vit)
        {
            resolveNearVertexFlag(mesh, *vit, vProp_flag);
        }
    }
}

void CMarchingCubesWorker::resolveVertexFlag(geometry::CMesh &mesh, geometry::CMesh::VertexHandle vertex, OpenMesh::VPropHandleT<int> &vProp_flag, OpenMesh::FPropHandleT<short> &fProp_flag)
{
    std::vector<short> directionGroups;
    int facesAroundVertex = 0;
    for (geometry::CMesh::VertexFaceIter vfit = mesh.vf_begin(vertex); vfit != mesh.vf_end(vertex); ++vfit)
    {
        facesAroundVertex++;

        bool directionGroupFound = false;
        short directionGroup = mesh.property(fProp_flag, vfit.handle());

        for (std::size_t d = 0; d < directionGroups.size(); ++d)
        {
            if (directionGroup == directionGroups[d])
            {
                directionGroupFound = true;
                break;
            }
        }

        if (!directionGroupFound)
        {
            directionGroups.push_back(directionGroup);
        }
    }

    int vertexType = VT_NONE;
    switch (directionGroups.size())
    {
    case 0:
        vertexType = VT_NONE;
        break;

    case 1:
        vertexType = VT_FLAT;
        break;

    case 2:
        vertexType = VT_EDGE;
        break;

    default:
        vertexType = VT_CORNER;
        break;
    }

    // vertex with two adjacent faces must be edge (or corner)
    if (facesAroundVertex <= 2 && vertexType == VT_FLAT)
    {
        vertexType = VT_EDGE;
    }

    // vertex wit only one adjacent face must be corner
    if (facesAroundVertex <= 1)
    {
        vertexType = VT_CORNER;
    }

    mesh.property(vProp_flag, vertex) = vertexType;
}

void CMarchingCubesWorker::resolveNearVertexFlag(geometry::CMesh &mesh, geometry::CMesh::VertexHandle vertex, OpenMesh::VPropHandleT<int> &vProp_flag)
{
    if (mesh.property(vProp_flag, vertex) != VT_FLAT)
    {
        return;
    }

    for (geometry::CMesh::VertexVertexIter vvit = mesh.vv_begin(vertex); vvit != mesh.vv_end(vertex); ++vvit)
    {
        geometry::CMesh::VertexHandle vvertex = vvit.handle();
        int vvflag = mesh.property(vProp_flag, vvertex);

        if ((vvflag == VT_EDGE) || (vvflag == VT_CORNER))
        {
            mesh.property(vProp_flag, vertex) = VT_NEAR;
            break;
        }
    }
}

void CMarchingCubesWorker::lockOpenEdges()
{
    for (std::set<std::pair<geometry::CMesh::VertexHandle, int> >::iterator it = m_openEdgeVertices.begin(); it != m_openEdgeVertices.end(); ++it)
    {
        m_mesh.status(it->first).set_locked(true);
    }
}

void CMarchingCubesWorker::unlockOpenEdges()
{
    for (std::set<std::pair<geometry::CMesh::VertexHandle, int> >::iterator it = m_openEdgeVertices.begin(); it != m_openEdgeVertices.end(); ++it)
    {
        m_mesh.status(it->first).set_locked(true);
    }
}

void CMarchingCubesWorker::reduceFlatAreas(int numOfIterations, bool eliminateNearVertices, float maxEdgeLength)
{
    lockOpenEdges();
    std::vector<geometry::CMesh::VertexHandle> vertices;
    reduceFlatAreas(m_mesh, vertices, numOfIterations, eliminateNearVertices, maxEdgeLength);
    updateOpenEdgeVertices();
    unlockOpenEdges();
    m_mesh.garbage_collection();
}

void CMarchingCubesWorker::reduceFlatAreas(geometry::CMesh &mesh, std::vector<geometry::CMesh::VertexHandle> &vertices, int numOfIterations, bool eliminateNearVertices, float maxEdgeLength)
{
    numOfIterations = numOfIterations < 1 ? 1 : numOfIterations;
    geometry::CMesh::VertexHandle eliminated_vertex; // handle of eliminated vertex

    // markup vertices of input mesh, divide them on geometry groups
    markupVertices(mesh, vertices);

    // if there is no "vProp_flag", something is terribly wrong
    OpenMesh::VPropHandleT<int> vProp_flag;
    mesh.get_property_handle(vProp_flag, "vProp_flag");
    assert(vProp_flag.is_valid());

    // mesh vertices cycle
    for (int l = 0; l < numOfIterations; ++l)
    {
        std::vector<geometry::CMesh::VertexHandle> randomVertices;

        if (vertices.size() == 0)
        {
            for (geometry::CMesh::VertexIter vit = mesh.vertices_begin(); vit != mesh.vertices_end(); ++vit)
            {
                if (!mesh.status(vit.handle()).deleted() && !mesh.status(vit.handle()).locked())
                {
                    randomVertices.push_back(vit.handle());
                }
            }
        }
        else
        {
            for (std::vector<geometry::CMesh::VertexHandle>::iterator vit = vertices.begin(); vit != vertices.end(); ++vit)
            {
                if (!mesh.status(*vit).deleted() && !mesh.status(*vit).locked())
                {
                    randomVertices.push_back(*vit);
                }
            }
        }

        std::random_shuffle(randomVertices.begin(), randomVertices.end());

        for (std::vector<geometry::CMesh::VertexHandle>::iterator vit = randomVertices.begin(); vit != randomVertices.end(); ++vit)
        {
            // save handle of eliminated vertex
            eliminated_vertex = *vit;

            // test actual vertex type
            switch (mesh.property(vProp_flag, *vit))
            {
            case VT_NEAR:
                if (eliminateNearVertices)
                {
                    eliminateFlatVertex(mesh, eliminated_vertex, maxEdgeLength);
                }
                else
                {
                    eliminateNearVertex(mesh, eliminated_vertex, maxEdgeLength);
                }
                break;

            default:
                break;
            }
        }

        for (std::vector<geometry::CMesh::VertexHandle>::iterator vit = randomVertices.begin(); vit != randomVertices.end(); ++vit)
        {
            // save handle of eliminated vertex
            eliminated_vertex = *vit;

            // test actual vertex type
            switch (mesh.property(vProp_flag, *vit))
            {
            case VT_FLAT:
                eliminateFlatVertex(mesh, eliminated_vertex, maxEdgeLength);
                break;

            case VT_EDGE:
                eliminateEdgeVertex(mesh, eliminated_vertex, maxEdgeLength);
                break;

            default:
                break;
            }
        }

        // delete entities marked for deletion
        mesh.garbage_collection();
    }

    // swap mesh edges to improve quality
    swapTriEdges(mesh);

    mesh.garbage_collection();
}

bool CMarchingCubesWorker::eliminateNearVertex(geometry::CMesh &mesh, geometry::CMesh::VertexHandle eliminate_vertex, float maxEdgeLength)
{
    OpenMesh::VPropHandleT<int> vProp_flag;
    if (!mesh.get_property_handle(vProp_flag, "vProp_flag"))
    {
        return false;
    }

    // gather info of all possible collapses
    std::map<geometry::CMesh::HalfedgeHandle, double> collapsableEdges;

    // gather info about neighbour vertices
    std::vector<geometry::CMesh::Point> vertices;
    for (geometry::CMesh::VertexVertexIter vvit = mesh.vv_begin(eliminate_vertex); vvit != mesh.vv_end(eliminate_vertex); ++vvit)
    {
        vertices.push_back(mesh.point(vvit.handle()));
    }

    // try every edge
    for (geometry::CMesh::VertexOHalfedgeIter vohit = mesh.voh_begin(eliminate_vertex); vohit != mesh.voh_end(eliminate_vertex); ++vohit)
    {
        geometry::CMesh::HalfedgeHandle halfedge = vohit.handle();
        geometry::CMesh::VertexHandle targetVertex = mesh.to_vertex_handle(halfedge);
        double quality = -1.0;

        // save flag
        int flag = mesh.property<int>(vProp_flag, targetVertex);

        // near vertex can be collapsed only into another near vertex
        if (flag != VT_NEAR)
        {
            continue;
        }

        // check adjacent faces
        bool normalChange = false;
        for (geometry::CMesh::VertexFaceIter vfit = mesh.vf_begin(eliminate_vertex); vfit != mesh.vf_end(eliminate_vertex); ++vfit)
        {
            geometry::CMesh::FaceHandle face = vfit.handle();
            bool hasTarget = false;
            std::vector<geometry::CMesh::VertexHandle> fvertices;

            // construct virtual face
            for (geometry::CMesh::FaceVertexIter fvit = mesh.fv_begin(face); fvit != mesh.fv_end(face); ++fvit)
            {
                geometry::CMesh::VertexHandle vertex = fvit.handle();

                if (vertex == targetVertex)
                {
                    hasTarget = true;
                }

                if (vertex != eliminate_vertex)
                {
                    fvertices.push_back(vertex);
                }
                else
                {
                    fvertices.push_back(targetVertex);
                }
            }

            // faces with target vertex will be deleted completely
            if (hasTarget)
            {
                continue;
            }

            // check for change of normal after possible collapse
            geometry::CMesh::Normal fnormal = mesh.normal(face);
            geometry::CMesh::Normal vfnormal = mesh.calc_face_normal(mesh.point(fvertices[0]), mesh.point(fvertices[1]), mesh.point(fvertices[2]));

            if ((fnormal | vfnormal) < allowedDot)
            {
                normalChange = true;
                break;
            }

            // everything seems alright for this face
            double faceQuality = mesh.quality(mesh.point(fvertices[0]), mesh.point(fvertices[1]), mesh.point(fvertices[2]));
            quality = quality < 0.0 ? faceQuality : vpl::math::getMin<double>(quality, faceQuality);
        }

        if (normalChange)
        {
            continue;
        }

        // collapse if possible (eliminate_vertex -> targetVertex)
        if (!mesh.is_collapse_ok(halfedge))
        {
            continue;
        }

        // if quality of this collapse is too low, don't do it
        if (quality < quality_limit)
        {
            continue;
        }

        if (maxEdgeLength > 0.0)
        {
            geometry::CMesh::Point targetPoint = mesh.point(mesh.to_vertex_handle(halfedge));
            bool lengthOk = true;
            for (std::vector<geometry::CMesh::Point>::iterator vit = vertices.begin(); vit != vertices.end(); ++vit)
            {
                geometry::CMesh::Point vec = targetPoint - *vit;
                if (vec.length() > maxEdgeLength)
                {
                    lengthOk = false;
                    break;
                }
            }
            if (!lengthOk)
            {
                continue;
            }

            quality /= vpl::math::getMax(1.0f, (mesh.point(eliminate_vertex) - targetPoint).length());
        }

        // add halfedge to list of collapsable edges
        collapsableEdges[halfedge] = quality;
    }

    // select the best collapse if it is possible
    geometry::CMesh::HalfedgeHandle halfedge;
    for (std::map<geometry::CMesh::HalfedgeHandle, double>::iterator hit = collapsableEdges.begin(); hit != collapsableEdges.end(); ++hit)
    {
        if ((!halfedge.is_valid()) || (hit->second > collapsableEdges[halfedge]))
        {
            halfedge = hit->first;
        }
    }

    // perform collapse
    if (halfedge.is_valid())
    {
        geometry::CMesh::VertexHandle targetVertex = mesh.to_vertex_handle(halfedge);
        geometry::CMesh::Point p1 = mesh.point(eliminate_vertex);
        geometry::CMesh::Point p2 = mesh.point(targetVertex);
        geometry::CMesh::Point v1 = p2 - p1;
        v1.normalize();

        // check if collapsed vertex lies on line formed by selected edge and some "opposite" edge
        bool ok = false;
        for (geometry::CMesh::VertexVertexIter vvit = mesh.vv_begin(eliminate_vertex); vvit != mesh.vv_end(eliminate_vertex); ++vvit)
        {
            geometry::CMesh::Point p0 = mesh.point(vvit.handle());
            geometry::CMesh::Point v0 = p1 - p0;
            v0.normalize();

            if (mesh.property<int>(vProp_flag, vvit.handle()) != VT_NEAR)
            {
                continue;
            }

            if ((v0 | v1) > allowedDot)
            {
                ok = true;
                break;
            }
        }

        if (!ok)
        {
            return false;
        }

        // save flag
        int flag = mesh.property<int>(vProp_flag, targetVertex);

        mesh.collapse(halfedge);

        // restore flag
        mesh.property<int>(vProp_flag, targetVertex) = flag;

        return true;
    }

    return false;
}

bool CMarchingCubesWorker::eliminateFlatVertex(geometry::CMesh &mesh, geometry::CMesh::VertexHandle eliminate_vertex, float maxEdgeLength)
{
    // get "vProp_flag" property
    OpenMesh::VPropHandleT<int> vProp_flag;
    if (!mesh.get_property_handle(vProp_flag, "vProp_flag"))
    {
        return false;
    }

    // gather info of all possible collapses
    std::map<geometry::CMesh::HalfedgeHandle, double> collapsableEdges;

    // gather info about neighbour vertices
    std::vector<geometry::CMesh::Point> vertices;
    for (geometry::CMesh::VertexVertexIter vvit = mesh.vv_begin(eliminate_vertex); vvit != mesh.vv_end(eliminate_vertex); ++vvit)
    {
        vertices.push_back(mesh.point(vvit.handle()));
    }

    // stack based allocator for fvertices vector
#ifndef _DEBUG
    const std::size_t stack_size = 3;
    geometry::CMesh::VertexHandle bufferVH[stack_size];
    typedef stack_allocator<geometry::CMesh::VertexHandle, stack_size> allocator_type;
#endif

    // try every edge
    for (geometry::CMesh::VertexOHalfedgeIter vohit = mesh.voh_begin(eliminate_vertex); vohit != mesh.voh_end(eliminate_vertex); ++vohit)
    {
        geometry::CMesh::HalfedgeHandle halfedge = vohit.handle();
        geometry::CMesh::VertexHandle targetVertex = mesh.to_vertex_handle(halfedge);
        if (mesh.property<int>(vProp_flag, targetVertex) == VT_NONE)
        {
            continue;
        }
        double quality = -1.0;

        // check adjacent faces
        bool normalChange = false;
        for (geometry::CMesh::VertexFaceIter vfit = mesh.vf_begin(eliminate_vertex); vfit != mesh.vf_end(eliminate_vertex); ++vfit)
        {
            geometry::CMesh::FaceHandle face = vfit.handle();
            bool hasTarget = false;
#ifdef _DEBUG
            std::vector<geometry::CMesh::VertexHandle> fvertices; 
#else   // use stack based allocator for better performance
            std::vector<geometry::CMesh::VertexHandle, allocator_type> fvertices((allocator_type(bufferVH)));
#endif
            
            // construct virtual face
            for (geometry::CMesh::FaceVertexIter fvit = mesh.fv_begin(face); fvit != mesh.fv_end(face); ++fvit)
            {
                geometry::CMesh::VertexHandle vertex = fvit.handle();

                if (vertex == targetVertex)
                {
                    hasTarget = true;
                }

                if (vertex != eliminate_vertex)
                {
                    fvertices.push_back(vertex);
                }
                else
                {
                    fvertices.push_back(targetVertex);
                }
            }

            // faces with target vertex will be deleted completely
            if (hasTarget)
            {
                continue;
            }

            assert(fvertices.size() == 3);

            // check for flip of normal after possible collapse
            geometry::CMesh::Normal fnormal = mesh.normal(face);
            geometry::CMesh::Normal vfnormal = mesh.calc_face_normal(mesh.point(fvertices[0]), mesh.point(fvertices[1]), mesh.point(fvertices[2]));

            if ((fnormal | vfnormal) < allowedDot)
            {
                normalChange = true;
                break;
            }

            // everything seems alright for this face
            double faceQuality = mesh.quality(mesh.point(fvertices[0]), mesh.point(fvertices[1]), mesh.point(fvertices[2]));
            quality = quality < 0.0 ? faceQuality : vpl::math::getMin<double>(quality, faceQuality);
        }

        if (normalChange)
        {
            continue;
        }

        // collapse if possible (eliminate_vertex -> targetVertex)
        if (!mesh.is_collapse_ok(halfedge))
        {
            continue;
        }

        // if quality of this collapse is too low, don't do it
        if (quality < quality_limit)
        {
            continue;
        }

        if (maxEdgeLength > 0.0)
        {
            geometry::CMesh::Point targetPoint = mesh.point(mesh.to_vertex_handle(halfedge));
            bool lengthOk = true;
            for (std::vector<geometry::CMesh::Point>::iterator vit = vertices.begin(); vit != vertices.end(); ++vit)
            {
                geometry::CMesh::Point vec = targetPoint - *vit;
                if (vec.length() > maxEdgeLength)
                {
                    lengthOk = false;
                    break;
                }
            }
            if (!lengthOk)
            {
                continue;
            }

            quality /= vpl::math::getMax(1.0f, (mesh.point(eliminate_vertex) - targetPoint).length());
        }

        // add halfedge to list of collapsable edges
        collapsableEdges[halfedge] = quality;
    }

    // select the best collapse if it is possible
    geometry::CMesh::HalfedgeHandle halfedge;
    for (std::map<geometry::CMesh::HalfedgeHandle, double>::iterator hit = collapsableEdges.begin(); hit != collapsableEdges.end(); ++hit)
    {
        if ((!halfedge.is_valid()) || (hit->second > collapsableEdges[halfedge]))
        {
            halfedge = hit->first;
        }
    }

    // perform collapse
    if (halfedge.is_valid())
    {
        geometry::CMesh::VertexHandle targetVertex = mesh.to_vertex_handle(halfedge);

        // save flag
        int flag = mesh.property<int>(vProp_flag, targetVertex);

        mesh.collapse(halfedge);

        // restore flag
        mesh.property<int>(vProp_flag, targetVertex) = flag;

        return true;
    }
    
    return false;
}

bool CMarchingCubesWorker::eliminateEdgeVertex(geometry::CMesh &mesh, geometry::CMesh::VertexHandle eliminate_vertex, float maxEdgeLength)
{
    OpenMesh::VPropHandleT<int> vProp_flag;
    if (!mesh.get_property_handle(vProp_flag, "vProp_flag"))
    {
        return false;
    }

    // gather info of all possible collapses
    std::map<geometry::CMesh::HalfedgeHandle, double> collapsableEdges;

    // gather info about neighbour vertices
    std::vector<geometry::CMesh::Point> vertices;
    for (geometry::CMesh::VertexVertexIter vvit = mesh.vv_begin(eliminate_vertex); vvit != mesh.vv_end(eliminate_vertex); ++vvit)
    {
        vertices.push_back(mesh.point(vvit.handle()));
    }

    // stack based allocator for fvertices vector
#ifndef _DEBUG
    const std::size_t stack_size = 3;
    geometry::CMesh::VertexHandle bufferVH[stack_size];
    typedef stack_allocator<geometry::CMesh::VertexHandle, stack_size> allocator_type;
#endif

    // try every edge
    for (geometry::CMesh::VertexOHalfedgeIter vohit = mesh.voh_begin(eliminate_vertex); vohit != mesh.voh_end(eliminate_vertex); ++vohit)
    {
        geometry::CMesh::HalfedgeHandle halfedge = vohit.handle();
        geometry::CMesh::VertexHandle targetVertex = mesh.to_vertex_handle(halfedge);
        double quality = -1.0;

        // save flag
        int flag = mesh.property<int>(vProp_flag, targetVertex);

        // edge vertex can be collapsed only into another edge vertex or into corner vertex
        if ((flag != VT_EDGE) && (flag != VT_CORNER))
        {
            continue;
        }

        // check adjacent faces
        bool normalChange = false;
        for (geometry::CMesh::VertexFaceIter vfit = mesh.vf_begin(eliminate_vertex); vfit != mesh.vf_end(eliminate_vertex); ++vfit)
        {
            geometry::CMesh::FaceHandle face = vfit.handle();
            bool hasTarget = false;
#ifdef _DEBUG
            std::vector<geometry::CMesh::VertexHandle> fvertices;
#else   // use stack based allocator for better performance
            std::vector<geometry::CMesh::VertexHandle, allocator_type> fvertices((allocator_type(bufferVH)));
#endif

            // construct virtual face
            for (geometry::CMesh::FaceVertexIter fvit = mesh.fv_begin(face); fvit != mesh.fv_end(face); ++fvit)
            {
                geometry::CMesh::VertexHandle vertex = fvit.handle();

                if (vertex == targetVertex)
                {
                    hasTarget = true;
                }

                if (vertex != eliminate_vertex)
                {
                    fvertices.push_back(vertex);
                }
                else
                {
                    fvertices.push_back(targetVertex);
                }
            }

            // faces with target vertex will be deleted completely
            if (hasTarget)
            {
                continue;
            }

            // check for change of normal after possible collapse
            geometry::CMesh::Normal fnormal = mesh.normal(face);
            geometry::CMesh::Normal vfnormal = mesh.calc_face_normal(mesh.point(fvertices[0]), mesh.point(fvertices[1]), mesh.point(fvertices[2]));

            if ((fnormal | vfnormal) < allowedDot)
            {
                normalChange = true;
                break;
            }

            // everything seems alright for this face
            double faceQuality = mesh.quality(mesh.point(fvertices[0]), mesh.point(fvertices[1]), mesh.point(fvertices[2]));
            quality = quality < 0.0 ? faceQuality : vpl::math::getMin<double>(quality, faceQuality);
        }

        if (normalChange)
        {
            continue;
        }

        // collapse if possible (eliminate_vertex -> targetVertex)
        if (!mesh.is_collapse_ok(halfedge))
        {
            continue;
        }

        // if quality of this collapse is too low, don't do it
        if (quality < quality_limit)
        {
            continue;
        }

        if (maxEdgeLength > 0.0)
        {
            geometry::CMesh::Point targetPoint = mesh.point(mesh.to_vertex_handle(halfedge));
            bool lengthOk = true;
            for (std::vector<geometry::CMesh::Point>::iterator vit = vertices.begin(); vit != vertices.end(); ++vit)
            {
                geometry::CMesh::Point vec = targetPoint - *vit;
                if (vec.length() > maxEdgeLength)
                {
                    lengthOk = false;
                    break;
                }
            }
            if (!lengthOk)
            {
                continue;
            }

            quality /= vpl::math::getMax(1.0f, (mesh.point(eliminate_vertex) - targetPoint).length());
        }

        // add halfedge to list of collapsable edges
        collapsableEdges[halfedge] = quality;
    }

    // select the best collapse if it is possible
    geometry::CMesh::HalfedgeHandle halfedge;
    for (std::map<geometry::CMesh::HalfedgeHandle, double>::iterator hit = collapsableEdges.begin(); hit != collapsableEdges.end(); ++hit)
    {
        if ((!halfedge.is_valid()) || (hit->second > collapsableEdges[halfedge]))
        {
            halfedge = hit->first;
        }
    }

    // perform collapse
    if (halfedge.is_valid())
    {
        geometry::CMesh::VertexHandle targetVertex = mesh.to_vertex_handle(halfedge);

        // save flag
        int flag = mesh.property<int>(vProp_flag, targetVertex);

        mesh.collapse(halfedge);

        // restore flag
        mesh.property<int>(vProp_flag, targetVertex) = flag;

        return true;
    }
    
    return false;
}

void CMarchingCubesWorker::swapTriEdges(geometry::CMesh &mesh)
{
    // precomputations for quality enhancement
    makeAllTrisQuality(mesh);

    OpenMesh::FPropHandleT<double> fProp_quality;
    OpenMesh::FPropHandleT<geometry::CMesh::EdgeHandle> fProp_maxEdge;
    OpenMesh::FPropHandleT<geometry::CMesh::FaceHandle> fProp_neighbour;
    OpenMesh::FPropHandleT<int> fProp_loop;

    mesh.get_property_handle(fProp_quality, "quality");
    mesh.get_property_handle(fProp_maxEdge, "maxEdge");
    mesh.get_property_handle(fProp_neighbour, "neighbour");
    mesh.get_property_handle(fProp_loop, "loop");

    int swap_number; // number of realized swaps
    int loop = 0;

    do
    {
        // initialization
        swap_number = 0;

        // mesh tris cycle
        for (geometry::CMesh::FaceIter fit = mesh.faces_begin(); fit != mesh.faces_end(); ++fit)
        {
            // swap actual tri to improve his quality (shape)
            if (swapTriImproveQuality(mesh, fit.handle(), loop, fProp_quality, fProp_maxEdge, fProp_neighbour, fProp_loop))
            {
                ++swap_number;
            }
        }

        loop++;
        std::cout << ", " << swap_number;
    } while (swap_number != 0);

    // remove properties
    mesh.remove_property(fProp_quality);
    mesh.remove_property(fProp_maxEdge);
    mesh.remove_property(fProp_neighbour);
    mesh.remove_property(fProp_loop);
}

bool CMarchingCubesWorker::swapTriImproveQuality(
    geometry::CMesh &mesh,
    geometry::CMesh::FaceHandle curr_tri,
    int loop,
    OpenMesh::FPropHandleT<double> fProp_quality,
    OpenMesh::FPropHandleT<geometry::CMesh::EdgeHandle> fProp_maxEdge,
    OpenMesh::FPropHandleT<geometry::CMesh::FaceHandle> fProp_neighbour,
    OpenMesh::FPropHandleT<int> fProp_loop)
{
    // do not attempt if there was no change last time
    if (loop > mesh.property<int>(fProp_loop, curr_tri))
    {
        return false;
    }

    // take max edge of tri
    geometry::CMesh::EdgeHandle max_edge = mesh.property<geometry::CMesh::EdgeHandle>(fProp_maxEdge, curr_tri);
    geometry::CMesh::HalfedgeHandle max_halfedge = mesh.halfedge_handle(max_edge, 0);

    // take neighbour tri by max edge
    geometry::CMesh::FaceHandle neighbour_tri = mesh.property<geometry::CMesh::FaceHandle>(fProp_neighbour, curr_tri);
    if (!neighbour_tri.is_valid())
    {
        return false;
    }

    // take vertices of edge
    geometry::CMesh::VertexHandle edge_v0 = mesh.from_vertex_handle(max_halfedge);
    geometry::CMesh::VertexHandle edge_v1 = mesh.to_vertex_handle(max_halfedge);

    // take rest vertices of both tris
    geometry::CMesh::VertexHandle curr_tri_vertex = mesh.rest_vertex(curr_tri, edge_v0, edge_v1);
    geometry::CMesh::VertexHandle neighbour_tri_vertex = mesh.rest_vertex(neighbour_tri, edge_v0, edge_v1);

    // take minimal actual quality
    double actual_quality_0 = mesh.property<double>(fProp_quality, curr_tri);
    double actual_quality_1 = mesh.property<double>(fProp_quality, neighbour_tri);
    double actual_quality = vpl::math::getMin<double>(actual_quality_0, actual_quality_1);

    // get points in space
    geometry::CMesh::Point p0, p1, p2, p3;
    p0 = mesh.point(curr_tri_vertex);
    p1 = mesh.point(neighbour_tri_vertex);
    p2 = mesh.point(edge_v1);
    p3 = mesh.point(edge_v0);

    // get normals of both tris to swap
    geometry::CMesh::Normal curr_normal = mesh.calc_face_normal(p0, p2, p3);
    geometry::CMesh::Normal neighbour_normal = mesh.calc_face_normal(p1, p3, p2);

    // test, if tris to swap are in plane
    if ((curr_normal | neighbour_normal) < allowedDot)
    {
        return false;
    }

    // make first variant working normal, quality and test results of swap
    geometry::CMesh::Normal work_normal = mesh.calc_face_normal(p0, p2, p1);
    double work_quality_0 = mesh.quality(p0, p2, p1);

    // test swap tri overturn because of swap
    double dot = curr_normal | work_normal;
    if (dot < allowedDot)
    {
        return false;
    }

    // test working tri area
    if (mesh.area(p0, p2, p1) < 0.000000001)
    {
        return false;
    }

    // make second variant working normal, quality and test results of swap
    work_normal = mesh.calc_face_normal(p0, p1, p3);
    double work_quality_1 = mesh.quality(p0, p1, p3);

    // test swap tri overturn because of swap
    if ((curr_normal | work_normal) < allowedDot)
    {
        return false;
    }

    // test working tri area
    if (mesh.area(p0, p1, p3) < 0.000000001)
    {
        return false;
    }

    // take minimal quality after swap
    double work_quality = vpl::math::getMin<double>(work_quality_0, work_quality_1);

    // compare actual and swap quality
    if (actual_quality >= work_quality)
    {
        return false;
    }

    // OpenMesh check
    if (!mesh.is_flip_ok(max_edge))
    {
        return false;
    }

    // register adjacent faces for recalculation of neighbuors and traversal in next pass
    std::vector<geometry::CMesh::FaceHandle> needRecalculationFaces;
    for (geometry::CMesh::FaceFaceIter ffit = mesh.ff_begin(curr_tri); ffit != mesh.ff_end(curr_tri); ++ffit)
    {
        geometry::CMesh::FaceHandle faceHandle = ffit.handle();
        if (!faceHandle.is_valid())
        {
            continue;
        }

        if (mesh.property<geometry::CMesh::FaceHandle>(fProp_neighbour, faceHandle) == curr_tri)
        {
            needRecalculationFaces.push_back(faceHandle);
        }
    }
    for (geometry::CMesh::FaceFaceIter ffit = mesh.ff_begin(neighbour_tri); ffit != mesh.ff_end(neighbour_tri); ++ffit)
    {
        geometry::CMesh::FaceHandle faceHandle = ffit.handle();
        if (!faceHandle.is_valid())
        {
            continue;
        }

        if (mesh.property<geometry::CMesh::FaceHandle>(fProp_neighbour, ffit.handle()) == neighbour_tri)
        {
            needRecalculationFaces.push_back(ffit.handle());
        }
    }

    // swap
    mesh.flip(max_edge);

    // recalculate properties for swapped faces
    mesh.update_normal(curr_tri);
    mesh.update_normal(neighbour_tri);
    mesh.property<double>(fProp_quality, curr_tri) = work_quality_0;
    mesh.property<double>(fProp_quality, neighbour_tri) = work_quality_1;
    mesh.property<geometry::CMesh::EdgeHandle>(fProp_maxEdge, curr_tri) = mesh.max_edge(curr_tri);
    mesh.property<geometry::CMesh::EdgeHandle>(fProp_maxEdge, neighbour_tri) = mesh.max_edge(neighbour_tri);
    mesh.property<geometry::CMesh::FaceHandle>(fProp_neighbour, curr_tri) = mesh.neighbour(curr_tri, mesh.property<geometry::CMesh::EdgeHandle>(fProp_maxEdge, curr_tri));
    mesh.property<geometry::CMesh::FaceHandle>(fProp_neighbour, neighbour_tri) = mesh.neighbour(neighbour_tri, mesh.property<geometry::CMesh::EdgeHandle>(fProp_maxEdge, neighbour_tri));
    mesh.property<int>(fProp_loop, curr_tri) = loop + 1;
    mesh.property<int>(fProp_loop, neighbour_tri) = loop + 1;

    // recalculate properties for adjacent faces
    for (int i = 0; i < (int)needRecalculationFaces.size(); ++i)
    {
        geometry::CMesh::FaceHandle currFace = needRecalculationFaces[i];
        mesh.property<geometry::CMesh::FaceHandle>(fProp_neighbour, currFace) = mesh.neighbour(currFace, mesh.property<geometry::CMesh::EdgeHandle>(fProp_maxEdge, currFace));
        mesh.property<int>(fProp_loop, currFace) = loop + 1;
    }

    // swap is realized
    return true;
}

void CMarchingCubesWorker::makeAllTrisQuality(geometry::CMesh &mesh)
{
    // add "quality" property
    OpenMesh::FPropHandleT<double> fProp_quality;
    OpenMesh::FPropHandleT<geometry::CMesh::EdgeHandle> fProp_maxEdge;
    OpenMesh::FPropHandleT<geometry::CMesh::FaceHandle> fProp_neighbour;
    OpenMesh::FPropHandleT<int> fProp_loop;
    mesh.add_property(fProp_quality, "quality");
    mesh.add_property(fProp_maxEdge, "maxEdge");
    mesh.add_property(fProp_neighbour, "neighbour");
    mesh.add_property(fProp_loop, "loop");
    
    // cycle of input mesh tris
    for (geometry::CMesh::FaceIter fit = mesh.faces_sbegin(); fit != mesh.faces_end(); ++fit)
    {
        mesh.property(fProp_quality, fit) = mesh.quality(fit);
        mesh.property(fProp_maxEdge, fit) = mesh.max_edge(fit);
        mesh.property(fProp_neighbour, fit) = mesh.neighbour(fit, mesh.property(fProp_maxEdge, fit));
        mesh.property(fProp_loop, fit) = 0;
    }
}
